Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Applies to:
SQL Server 2025 (17.x) Preview
Creates a JSON index on a specified table and column in SQL Server 2025 (17.x) Preview.
JSON indexes:
- Can be created before there's data in the table.
- Can be created on tables in another database by specifying a qualified database name.
- Require the table to have a clustered primary key.
- Can't be specified on indexed views.
Note
Creating JSON indexes is currently in preview and only available in SQL Server 2025 (17.x) Preview.
Transact-SQL syntax conventions
Syntax
CREATE JSON INDEX name ON table_name (json_column_name)
[ FOR ( sql_json_path [ , ...n ] ) ]
[ WITH ( <json_index_option> [ , ...n ] ) ]
[ ON { filegroup_name | "default" } ]
[ ; ]
<object> ::=
{ database_name.schema_name.table_name | schema_name.table_name | table_name }
<sql_json_path> ::=
{ character_string_literal }
<json_index_option> ::=
{
FILLFACTOR = fillfactor
| DROP_EXISTING = { ON | OFF }
| ONLINE = OFF
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| DATA_COMPRESSION = { NONE | ROW | PAGE }
}
Arguments
index_name
The name of the index. Index names must be unique within a table but don't have to be unique within a database. Index names must follow the rules of identifiers.
ON <object> ( json_column_name )
Specifies the object (database, schema, or table) on which the index is to be created, and the name of the json column.
json_column_name
The name of the column of json data type in
table_name
that contains zero or more of the specified SQL/JSON paths.sql_json_path
The SQL/JSON path that needs to be extracted and indexed from
json_column_name
. The default forsql_json_path
is$
.- Recursively indexes all keys/values from the specified path onward.
- Supports up to 128 levels in the JSON document path.
- Doesn't allow overlapping.
For example,
$.a
and$.a.b
raise an error, since the path$.a
recursively includes all the paths and the user intent is unclear.
ON filegroup_name
Creates the specified index on the specified filegroup. If no ___location is specified and the table isn't partitioned, the index uses the same filegroup as the underlying table. The filegroup must already exist.
ON "default"
Creates the specified index on the default filegroup.
The term default, in this context, isn't a keyword. It's an identifier for the default filegroup and must be delimited, as in ON "default"
or ON [default]
. If "default"
is specified, the QUOTED_IDENTIFIER
option must be ON
for the current session. This is the default setting. For more information, see SET QUOTED_IDENTIFIER.
<object>:: =
The fully qualified or non-fully qualified object to be indexed.
database_name
The name of the database.
schema_name
The name of the schema to which the table belongs.
table_name
The name of the table to be indexed.
FILLFACTOR = fillfactor
Specifies a percentage that indicates how full the Database Engine should make the leaf level of each index page during index creation or rebuild. fillfactor must be an integer value from 1
to 100
. The default is 0
. If fillfactor is 100
or 0
, the Database Engine creates indexes with leaf pages filled to capacity.
Note
Fill factor values 0
and 100
are the same in all respects.
The FILLFACTOR
setting applies only when the index is created or rebuilt. The Database Engine doesn't dynamically keep the specified percentage of empty space in the pages. To view the fill factor setting, use the sys.indexes catalog view.
Creating a clustered index with a FILLFACTOR
less than 100
affects the amount of storage space the data occupies, because the Database Engine redistributes the data when it creates the clustered index.
For more information, see Specify Fill Factor for an Index.
DROP_EXISTING = { ON | OFF }
Specifies that the named, preexisting spatial index is dropped and rebuilt. The default is OFF
.
ON
The existing index is dropped and rebuilt. The index name specified must be the same as a currently existing index; however, the index definition can be modified. For example, you can specify different columns, sort order, partition scheme, or index options.
OFF
An error is displayed if the specified index name already exists.
The index type can't be changed by using DROP_EXISTING
.
ONLINE = OFF
Specifies that underlying tables and associated indexes aren't available for queries and data modification during the index operation. In this version of SQL Server, online index builds aren't supported for JSON indexes. If this option is set to ON
for a JSON index, an error is raised. Either omit the ONLINE
option or set ONLINE
to OFF
.
An offline index operation that creates, rebuilds, or drops a JSON index, acquires a Schema modification (Sch-M) lock on the table. This prevents all user access to the underlying table for the duration of the operation.
Online index operations aren't available in every edition of SQL Server.
For a list of features supported by the editions of SQL Server on Windows, see:
- Editions and supported features of SQL Server 2025 Preview
- Editions and supported features of SQL Server 2022
- Editions and supported features of SQL Server 2019
- Editions and supported features of SQL Server 2017
- Editions and supported features of SQL Server 2016
ALLOW_ROW_LOCKS = { ON | OFF }
Specifies whether row locks are allowed. The default is ON
.
ON
Row locks are allowed when accessing the index. The Database Engine determines when row locks are used.
OFF
Row locks aren't used.
ALLOW_PAGE_LOCKS = { ON | OFF }
Specifies whether page locks are allowed. The default is ON
.
ON
Page locks are allowed when accessing the index. The Database Engine determines when page locks are used.
OFF
Page locks aren't used.
MAXDOP = max_degree_of_parallelism
Overrides the max degree of parallelism
configuration option for the duration of the index operation. Use MAXDOP
to limit the number of processors used in a parallel plan execution. The maximum is 64 processors.
Important
Although the MAXDOP
option is syntactically supported, CREATE SPATIAL INDEX
currently always uses only a single processor.
max_degree_of_parallelism can be one of the following values.
Value | Description |
---|---|
1 |
Suppresses parallel plan generation. |
>1 |
Restricts the maximum number of processors used in a parallel index operation to the specified number or fewer based on the current system workload. |
0 (default) |
Uses the actual number of processors or fewer based on the current system workload. |
For more information, see Configure Parallel Index Operations.
Parallel index operations aren't available in every edition of SQL Server.
For a list of features supported by the editions of SQL Server on Windows, see:
- Editions and supported features of SQL Server 2025 Preview
- Editions and supported features of SQL Server 2022
- Editions and supported features of SQL Server 2019
- Editions and supported features of SQL Server 2017
- Editions and supported features of SQL Server 2016
DATA_COMPRESSION = { NONE | ROW | PAGE }
Determines the level of data compression used by the index.
NONE
No compression used on data by the index
ROW
Row compression used on data by the index
PAGE
Page compression used on data by the index
Remarks
Every option can be specified only once per CREATE JSON INDEX
statement. Specifying a duplicate of any option raises an error.
[ ON { filegroup_name | "default" } ]
If you specify a filegroup for a JSON index, the index is placed on that filegroup, regardless of the partitioning scheme of the table.
For more information about creating indexes, see the Remarks section in CREATE INDEX.
Predicates supported with a JSON index
Searching operations on JSON documents contained in a json column in a table can be optimized if a JSON index exists on the json column. The JSON index is used in queries with various JSON function-based expressions.
The following examples use the Sales.SalesOrderHeader
table in the AdventureWorks2022
database with a json column called Info
. The Info
column is created as a json type. A JSON index is also created on the Info
column with default settings. The following code sample shows the CREATE JSON INDEX
statement:
CREATE JSON INDEX sales_info_idx ON Sales.SalesOrderHeader(Info);
For the sample search expressions, use the following JSON documents as data:
SalesOrderNumber | Info |
---|---|
437 |
{"Customer":{"Name":"Kelsey Raje","ID":16517,"Type":"IN"},"Order":{"ID":43710,"Number":"SO43710","CreationDate":"2011-06-02T00:00:00","TotalDue":3953.9884}} |
643 |
{"Customer":{"Name":"Aaron Campbell","ID":16167,"Type":"IN"},"Order":{"ID":64304,"Number":"SO64304","CreationDate":"2014-01-16T00:00:00","TotalDue":36.0230, "IsProcessed": true}} |
JSON_PATH_EXISTS function
Use the JSON_PATH_EXISTS function to test if a specified SQL/JSON path exists in a JSON document.
This query demonstrates JSON_PATH_EXISTS
on a json column that can be optimized using a JSON index:
SELECT COUNT(*)
FROM Sales.SalesOrderHeader
WHERE JSON_PATH_EXISTS(Info, '$.Order.IsProcessed') = 1;
JSON index is supported with JSON_PATH_EXISTS
predicate and the following operators:
- Comparison operators (
=
) IS [NOT] NULL
predicate (Not currently supported)
JSON_VALUE function
Use the JSON_VALUE to extract the JSON text / scalar value in a specified SQL/JSON path in a JSON document. The following queries show how a JSON_VALUE
expression on a json column can be optimized using a JSON index.
Equality search for a JSON string in an object property:
SELECT COUNT(*) FROM Sales.SalesOrderHeader WHERE JSON_VALUE(Info, '$.Customer.Type') = 'IN';
Equality search for a JSON number in an object property after converting the value to an int data type:
SELECT * FROM Sales.SalesOrderHeader WHERE JSON_VALUE(Info, '$.Customer.ID' RETURNING INT) = 16167;
Range search for a JSON number in an object property after converting the value to an int data type:
SELECT * FROM Sales.SalesOrderHeader WHERE JSON_VALUE(Info, '$.Customer.ID' RETURNING INT) IN (16167, 16517);
Range search for a JSON number in an object property after converting the value to a decimal data type:
SELECT * FROM Sales.SalesOrderHeader WHERE JSON_VALUE(Info, '$.Order.TotalDue RETURNING decimal(20, 4)) BETWEEN 1000 and 2000;
The JSON index is supported with a JSON_VALUE
predicate, and the following operators:
- Comparison operators (
=
) LIKE
predicate (not currently supported)IS [NOT] NULL
predicate (not currently supported)
JSON_CONTAINS function
The JSON_CONTAINS function supports easy searching of JSON values in a JSON document that can use a JSON index if present on a json column. This function can be used to test if a JSON scalar value, object, or array is contained in the specified SQL/JSON path in a JSON document. The search values specified as SQL scalar types are converted per existing SQL/JSON type conversions. These rules are defined in the behavior section.
Requirement
A clustering key is required on the table that contains the JSON column. An error is raised if the clustering key is absent. The clustering key is limited to 31 columns and the maximum size of the index key should be less than 128 bytes.
Permissions
The user must have ALTER
permission on the table, or be a member of the sysadmin fixed server role, or the db_ddladmin and db_owner fixed database roles.
Limitations
The following limitations exist for the JSON index statement:
- Only one JSON index can be created on a json column in a table.
- You can create up to 249 JSON indexes in a table. Creating more than one JSON index on a specific JSON column isn't supported.
- A JSON index can't be created on computed json columns.
- A JSON index can't be created on json columns in a view, table-valued variable, or memory optimized table.
- A JSON index can be created or altered only in an offline manner.
- JSON paths can't overlap in the index definition. For example,
$a
and$a.b
overlap, and aren't allowed in theCREATE JSON INDEX
statement. - Modification of paths requires recreating the JSON index.
- JSON indexes aren't supported in index hints.
- The data compression option isn't supported.
Examples
A. Create a JSON index on a JSON column
The following example creates a table named docs
that contains a json type column, content
. The example then creates a JSON index, json_content_index
, on the content
column. The example creates the json index on the entire JSON document or all SQL/JSON paths in the JSON document.
DROP TABLE IF EXISTS docs;
CREATE TABLE docs (content JSON, id INT PRIMARY KEY);
CREATE JSON INDEX json_content_index ON docs(content);
A. Create a JSON index on a JSON column with specific paths
The following example creates a table named docs
that contains a json type column, content
. The example then creates a JSON index, json_content_index
, on the content
column. The example creates the json index on specific SQL/JSON paths in the JSON document.
The example also sets the index FILLFACTOR to 80
.
DROP TABLE IF EXISTS docs;
CREATE TABLE docs (content JSON, id INT PRIMARY KEY);
CREATE JSON INDEX json_content_index
ON docs(content) FOR ('$.a', '$.b')
WITH (FILLFACTOR = 80);