Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

PL/SQL & T-SQL (Oracle and SQL Server)  Module 8 – Production T-SQL: concurrency, security, new features and tests

SQL Server 2025: JSON, regex and new functions

Store JSON in the SQL Server 2025 json type, build it from rows, clean and check text with the REGEXP functions, and see which features need level 170.

  • Intermediate
  • 10 minutes
  • Examples run with oracle-free 23.26.3.0.0, mssql 17.0.5005.3, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 4 of 10 minutes free
Free: SQL Server 2025 stores JSON in a type that refuses what is not JSON · The REGEXP functions clean and check text inside the database · Most new features work at any level, a few need level 170. With a pass: 2 in-depth parts and 3 interview answers, about 6 minutes.

What you will learn

  • Store and query JSON with the json type, JSON_VALUE, JSON_QUERY, OPENJSON and FOR JSON
  • Build JSON from rows with JSON_OBJECTAGG and JSON_ARRAYAGG, and choose what a NULL becomes
  • Validate and clean text with REGEXP_LIKE, REGEXP_REPLACE and the other REGEXP functions
  • Check which new features need a database compatibility level before you depend on them

Before you start

What you’ll learn · Before you start

What you will learn

  • Store and query JSON with the json type, JSON_VALUE, JSON_QUERY, OPENJSON and FOR JSON
  • Build JSON from rows with JSON_OBJECTAGG and JSON_ARRAYAGG, and choose what a NULL becomes
  • Validate and clean text with REGEXP_LIKE, REGEXP_REPLACE and the other REGEXP functions
  • Check which new features need a database compatibility level before you depend on them

Before you start

On this page

A partner sends 8 customer rows, and only 4 of them can be loaded: mobile numbers arrive as +91-98470-11111 or 98450 12345, one vehicle number is too short, and one preferences value is a bare JSON string. SQL Server 2025 can check all of it in T-SQL, with a json type that refuses what is not JSON and functions for regular expression: A pattern that describes a set of strings, such as ten digits that start with 6 to 9. SQL Server 2025's REGEXP functions test, extract, count and replace matches of patterns written in RE2 syntax..

You will store and read JSON in the new type, see how rows become JSON, clean and test the import’s text with the REGEXP functions, and check which of these features depend on the database’s compatibility level.

SQL Server 2025 stores JSON in a type that refuses what is not JSON

Until SQL Server 2025, JSON lived in nvarchar(max) columns, and nothing stopped a broken document unless a CHECK with ISJSON was added. The new json data type: The SQL Server 2025 type for JSON documents. It stores a parsed binary form, accepts only a JSON object or array at the top level and refuses other text with error 13609. stores documents in a binary format and accepts only a JSON object or array at the top level. Microsoft’s page names the refusals: a JSON scalar such as "email", or an empty string, fails with error 13609, and so does text that is not JSON at all. A duplicate key is not an error: the type keeps the first value. The type works at every compatibility level.

Six functions read JSON or build it:

Function What it gives you
JSON_VALUE one scalar from a path, such as $.lang
JSON_QUERY an object or an array from a path
OPENJSON rows and columns from a document
FOR JSON JSON text from a query’s rows
JSON_OBJECTAGG one object from many rows’ keys and values
JSON_ARRAYAGG one array from many rows’ values

Checkpoint

Check: The json type and the JSON functions

  1. Question 1 of 2 Which of these inserts into a json column fails with error 13609?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: N'"email"', a JSON string on its own

    An empty string and text that is not JSON at all fail with the same error.

  2. Question 2 of 2 A query needs the language of each customer, the value at $.lang, as one column. Which function gives it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: JSON_VALUE, which returns one scalar from a path

    OPENJSON is the choice when several values of one document are needed as columns.

The REGEXP functions clean and check text inside the database

SQL Server 2025’s regular expressions follow RE2, Google’s library, and come in two kinds. REGEXP_LIKE returns true or false, so it goes where T-SQL expects a condition: WHERE, CASE WHEN or a CHECK constraint. REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_COUNT return a value, and REGEXP_MATCHES and REGEXP_SPLIT_TO_TABLE return rows. Step through one row of the import:

Interactive · stage by stage One import row, cleaned, checked, then loaded or set aside Step down the stages with row 2 of the import. The branch is the way out for a row that fails a check.
One import row, cleaned, checked, then loaded or set aside, step 6 of 6. Showing The row as sent, Keep only the digits (919847011111), Check the shape (9847011111), Check the preferences, Load the clean row, Set the row aside. In focus: Set the row aside.a check failsThe row as sent+91-98470-11111Keep only the digits919847011111Check the shape9847011111Check the preferencesthe json typeLoad the clean rowinto the target tableSet the row asidewith its reasons

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The row arrives as text. Row 2 of the import carries its mobile number as +91-98470-11111, with a country code, hyphens and a plus sign that no column should keep.
  2. Only the digits stay. REGEXP_REPLACE with the pattern of every character that is not a digit leaves 919847011111, twelve digits.
  3. The shape is checked. REGEXP_LIKE tests the digits against an optional 91 or 0 followed by ten digits that start with 6 to 9. Row 2 fits, and its number is 9847011111.
  4. The preferences are JSON. The preferences text goes into a json value, which accepts only an object or an array. Row 2 sends an object, so it is stored.
  5. The clean row is loaded. Every check has passed, so the cleaned values are inserted. The raw text stays in the staging table, so the cleaning can be checked later.
  6. A failing row is set aside. Row 7 reduces to 1234567890, which starts with 1, so it fails the shape check. It goes to the rejects with the reason, and nobody guesses a number for it.

The model applies the same three teaching shapes to all 8 rows, with the json type’s rules for the preferences. Its patterns use only what RE2 and Python read alike; press Edit and add a row of your own:

Eight import rows cleaned and checked, modelled in Python Python · model/import_check.py
"""Clean and check a partner's import of 8 customer rows, the way the lesson's T-SQL does it.

Each mobile number keeps only its digits and may start with 91 or 0; what is left must be 10 digits that start with
6, 7, 8 or 9. Vehicle numbers lose their spaces and hyphens and must have the shape of two letters, one or two
digits, up to three letters and four digits. PIN codes keep their digits and must be six digits that do not start
with 0. These are teaching shapes, not official validation rules. The preferences must be JSON that SQL Server 2025's
json type accepts: an object or an array at the top level; a duplicate key keeps its first value.

The patterns use only what RE2, the library behind SQL Server's REGEXP functions, and Python's re both read the same
way: anchors, character classes, groups and counted repeats. Press Edit and add a row of your own to ROWS.
"""

import json
import re

MOBILE = r"^(91|0)?([6-9][0-9]{9})$"
VEHICLE = r"^[A-Z]{2}[0-9]{1,2}[A-Z]{0,3}[0-9]{4}$"
PIN = r"^[1-9][0-9]{5}$"

ROWS = [  # (row, mobile as sent, vehicle as sent, PIN as sent, preferences as sent)
    (1, "98450 12345", "KA 01 AB 1234", "560001", '{"lang":"kn","sms":true}'),
    (2, "+91-98470-11111", "KL07CD4321", "682001", '{"lang":"ml","sms":false}'),
    (3, "0522 2611234", "UP32 EF 0001", "226001", '{"lang":"hi"}'),
    (4, "9840012345", "TN 09 GH 5678", "600001", '{"lang":"ta","sms":true,"sms":false}'),
    (5, "98300-55555", "WB-02-AK-7777", "700001", '"email"'),
    (6, "+91 99999 00000", "DL 3C AB 1234", "110001", '{"lang":"en","sms":true}'),
    (7, "12345 67890", "MH 12 DE 1433", "411001", "[]"),
    (8, "8888 777 666", "GJ 1 KL 99", "380 001", "{}"),
]


def first_key_wins(pairs):
    out = {}
    for key, value in pairs:
        out.setdefault(key, value)
    return out


def refuse_constant(name):
    raise ValueError(f"{name} is not JSON")


def as_json(text):
    """The json type's rules: an object or an array at the top level, duplicate keys keep the first value."""
    try:
        value = json.loads(text, object_pairs_hook=first_key_wins, parse_constant=refuse_constant)
    except ValueError:
        return None, "not valid JSON"
    if not isinstance(value, (dict, list)):
        return None, "a JSON scalar, not an object or an array"
    return json.dumps(value, separators=(",", ":")), None


loaded, rejected, changed = 0, 0, 0
print(f"{'row':<4}{'mobile':<12}{'vehicle':<12}{'PIN':<8}{'preferences':<27}result")
for row, mobile_raw, vehicle_raw, pin_raw, prefs_raw in ROWS:
    reasons = []
    digits = re.sub(r"[^0-9]", "", mobile_raw)
    m = re.search(MOBILE, digits)
    mobile = m.group(2) if m else None
    if mobile is None:
        reasons.append("mobile")
    vehicle = re.sub(r"[ -]", "", vehicle_raw).upper()
    if not re.search(VEHICLE, vehicle):
        reasons.append("vehicle")
    pin = re.sub(r"[^0-9]", "", pin_raw)
    if not re.search(PIN, pin):
        reasons.append("PIN")
    prefs, why = as_json(prefs_raw)
    if prefs is None:
        reasons.append(f"preferences ({why})")
    changed += (mobile not in (None, mobile_raw)) + (vehicle != vehicle_raw) + (pin != pin_raw)
    changed += prefs is not None and prefs != prefs_raw
    result = "load" if not reasons else "reject: " + ", ".join(reasons)
    loaded += not reasons
    rejected += bool(reasons)
    print(f"{row:<4}{mobile or '-':<12}{vehicle:<12}{pin:<8}{(prefs or '-'):<27}{result}")
print(f"{loaded} rows to load, {rejected} to reject; cleaning changed {changed} values of the rows")

Output

row mobile      vehicle     PIN     preferences                result
1   9845012345  KA01AB1234  560001  {"lang":"kn","sms":true}   load
2   9847011111  KL07CD4321  682001  {"lang":"ml","sms":false}  load
3   -           UP32EF0001  226001  {"lang":"hi"}              reject: mobile
4   9840012345  TN09GH5678  600001  {"lang":"ta","sms":true}   load
5   9830055555  WB02AK7777  700001  -                          reject: preferences (a JSON scalar, not an object or an array)
6   9999900000  DL3CAB1234  110001  {"lang":"en","sms":true}   load
7   -           MH12DE1433  411001  []                         reject: mobile
8   8888777666  GJ1KL99     380001  {}                         reject: vehicle
4 rows to load, 4 to reject; cleaning changed 14 values of the rows

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 import_check.py

Cleaning changed 14 values without a guess. Rows 3 and 7 are not mobile numbers once their digits are read, row 8’s vehicle number has two digits where four belong, and row 5’s preferences are a JSON string, not an object. Row 4 sends sms twice and keeps the first value. A regex tester helps to try a pattern on more samples before it goes into a constraint.

Checkpoint

Check: Cleaning and checking with REGEXP

  1. Question 1 of 2 Where can REGEXP_LIKE be used?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Where T-SQL expects a condition, such as WHERE, CASE WHEN or a CHECK constraint

    REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_COUNT return values; REGEXP_MATCHES and REGEXP_SPLIT_TO_TABLE return rows.

  2. Question 2 of 2 Row 7 of the import sends 12345 67890 as its mobile number. What should the load do with it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Set the row aside with the reason, because ten digits that start with 1 are not a mobile number

    The model loads 4 rows and sets 4 aside, each with its reason.

Most new features work at any level, a few need level 170

The compatibility level: A setting of each SQL Server database that decides which Transact-SQL behaviours and features it uses; new databases get 170 on SQL Server 2025, 160 on 2022 and 150 on 2019. More in “PL/SQL and T-SQL: what they are and where they run” decides some of what a 2025 server offers a database. Microsoft’s pages say which features need a level and which do not:

Feature Level it needs
REGEXP_LIKE, REGEXP_MATCHES, REGEXP_SPLIT_TO_TABLE 170
REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR, REGEXP_COUNT any
json and vector types, JSON_OBJECTAGG, JSON_ARRAYAGG any
OPENJSON 130 or more
EDIT_DISTANCE, JARO_WINKLER_SIMILARITY preview: PREVIEW_FEATURES on

The other additions are small and useful: the double-pipe operator joins strings, PRODUCT multiplies a column’s values, CURRENT_DATE returns today’s date, UNISTR turns escapes such as \20B9 into characters, BASE64_ENCODE writes binary as text, and SUBSTRING may leave out its length. The two similarity functions measure how alike two strings are. One older function belongs beside them for Indian data: FORMAT with the culture en-IN writes numbers with Indian digit grouping.

Checkpoint

Check: Which features need level 170

  1. One question A database on SQL Server 2025 stays at compatibility level 160. Which statement is right?

    Choose one answer.

    Show the answer: One question

    Answer: REGEXP_REPLACE and the json type work, but REGEXP_LIKE does not

    Check the level with sys.databases before a script depends on a feature that needs it.

JSON in depth: the type against nvarchar, reads, and JSON built from rows Pro: opens with a Pro, Premium or Ultimate pass

The json type changes what a database can promise about its documents, and the read and build functions decide how much work each query does. This part works through the lesson’s events table and compares the two ways to store JSON.

In this part:

  • The json type against nvarchar(max) with ISJSON
  • Reading the events: JSON_VALUE, JSON_QUERY and OPENJSON WITH
  • Building JSON for an API: aggregates and the NULL clause

3 runnable examples (Python, SQL) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Regex and the new functions in practice, with the import cleaned in T-SQL Pro: opens with a Pro, Premium or Ultimate pass

The model’s rules become one set-based statement on SQL Server 2025. This part writes it, shows what a regular expression can and cannot do for an index, and puts the smaller new functions to work.

In this part:

  • The import cleaned and checked in one statement
  • What RE2 refuses, and when a pattern can seek an index
  • The smaller additions, each on a lab value

2 runnable examples (Python, SQL) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: clean, check and read the import

Each question shows a value from the import, a function or a level. Decide what SQL Server 2025 does with it:

Practice that adapts to you

Practise this lesson

8 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Which value can a json column store?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: '[]'

    Objects and arrays only, at the top level.

  2. Question 2 of 8 Row 4 sends {"lang":"ta","sms":true,"sms":false} into a json column. What is stored?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: The document with "sms":true, the first value, and no error

    The lesson's model keeps the first key on purpose, to behave like the type.

  3. Question 3 of 8 A database still runs on SQL Server 2022, with JSON in an nvarchar(max) column. How do you stop broken documents from being stored?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: A CHECK constraint with ISJSON(col) = 1

    Before the json type, a string column took any text, and a CHECK with ISJSON was what kept broken documents out.

  4. Question 4 of 8 Which REGEXP function returns rows rather than one value?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: REGEXP_SPLIT_TO_TABLE

    The two table-valued functions go in a FROM clause.

  5. Question 5 of 8 What does REGEXP_REPLACE('98300-55555', '[^0-9]', '') return?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: 9830055555

    Row 5's number is cleaned the same way, and then passes the shape check.

  6. Question 6 of 8 Row 8's mobile number and PIN code pass. Why is the row still set aside?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: Its vehicle number, GJ1KL99, has two digits where the shape needs four

    A shape check is a teaching rule here, not an official validation rule for vehicle numbers.

  7. Question 7 of 8 Which compatibility level does the json type need?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: Any level, on SQL Server 2025

    The vector type works at every level too.

  8. Question 8 of 8 A script splits a list of tags with REGEXP_SPLIT_TO_TABLE in a database at level 160. What happens?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: It fails, because the table-valued REGEXP functions need level 170

    The scalar REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_COUNT are the ones that need no level.

Interview questions

Three questions follow, about a regex check that fails on one server, a choice of type for an events table and PIN codes taken from addresses typed as plain text; write your answer before you open each first step:

  1. Mid to senior

    A validation procedure that uses REGEXP_LIKE works on your development database, but on the staging server, also SQL Server 2025, its deployment fails with error 195. The same release uses REGEXP_REPLACE and a json column without trouble. Explain the difference, and say what you would do this week and what before the next release.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A new events table on SQL Server 2025 will receive about 2 million JSON documents a day from mobile apps, and reports read three values from each by path. A colleague proposes nvarchar(max) with an ISJSON check, as on the old servers. Would you use the json type instead? Defend the choice and say what you would check first.

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A nightly job receives 200,000 shop addresses as plain text and must store each shop's PIN code in its own column. Some addresses hold no PIN code, some hold two, and some write it as 411 001. How would you extract it on SQL Server 2025, and where do rows without exactly one go?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What does SQL Server 2025's json type accept and refuse?

    It stores documents in a binary format and accepts only a JSON object or array at the top level: a JSON scalar such as a bare string, an empty string, or text that is not JSON fails with error 13609. A duplicate key is not an error, the type keeps the first value, and the type works at every compatibility level.

  2. Which function reads one scalar from JSON, which reads a subtree, and which turns a document into rows?

    JSON_VALUE returns one scalar from a path such as $.lang, JSON_QUERY returns an object or an array from a path, and OPENJSON returns rows and columns from a document.

  3. Which three things build JSON from rows?

    FOR JSON turns a query's rows into JSON text, JSON_OBJECTAGG builds one object from many rows' keys and values, and JSON_ARRAYAGG builds one array from many rows' values.

  4. Which REGEXP function is a condition, and what do the others return?

    REGEXP_LIKE returns true or false, so it goes where T-SQL expects a condition: WHERE, CASE WHEN or a CHECK constraint. REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_COUNT return a value, and REGEXP_MATCHES and REGEXP_SPLIT_TO_TABLE return rows. All follow RE2.

  5. Which of the 2025 features need compatibility level 170?

    Only REGEXP_LIKE, REGEXP_MATCHES and REGEXP_SPLIT_TO_TABLE. REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_COUNT work at any level, as do the json and vector types, JSON_OBJECTAGG and JSON_ARRAYAGG; OPENJSON needs 130 or more.

  6. Which two string similarity functions does the lesson use, and what switches them on?

    EDIT_DISTANCE and JARO_WINKLER_SIMILARITY, two of SQL Server 2025's four fuzzy string matching functions, which measure how alike two strings are; all four are previews and need the PREVIEW_FEATURES setting on.

  7. Name four smaller additions of SQL Server 2025.

    The double-pipe operator joins strings, PRODUCT multiplies a column's values, CURRENT_DATE returns today's date, UNISTR turns escapes into characters, BASE64_ENCODE writes binary as text, and SUBSTRING may leave out its length.

  8. What does the lesson's import do with a row whose number or code does not match its pattern?

    It keeps the row with the reason it failed rather than guessing a value: digits that do not make a mobile number, a vehicle code with two digits where four belong, and a preferences value that is a JSON string instead of an object are all reported, not repaired.

Key takeaways

  • SQL Server 2025’s json type accepts only a JSON object or array, refuses everything else with error 13609 and keeps the first value of a duplicate key.
  • REGEXP_LIKE is a condition, the other REGEXP functions return values or rows, and all of them follow RE2.
  • Only REGEXP_LIKE and the two table-valued REGEXP functions need compatibility level 170; the json type works at every level.
  • Clean an import with patterns and keep the rows that fail with their reasons, rather than guessing a value.

Terms in this lesson

json data type
The SQL Server 2025 type for JSON documents. It stores a parsed binary form, accepts only a JSON object or array at the top level and refuses other text with error 13609.
regular expression
A pattern that describes a set of strings, such as ten digits that start with 6 to 9. SQL Server 2025's REGEXP functions test, extract, count and replace matches of patterns written in RE2 syntax.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.