{"id":14661,"date":"2016-01-07T09:30:00","date_gmt":"2016-01-07T17:30:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/dataplatforminsider\/2016\/01\/07\https://www.microsoft.com/json-in-sql-server-2016-part-3-of-4\/"},"modified":"2024-01-22T22:52:28","modified_gmt":"2024-01-23T06:52:28","slug":"json-in-sql-server-2016-part-3-of-4","status":"publish","type":"post","link":"https:\/\/www.microsoft.com\/en-us\/sql-server\/blog\/2016\/01\/07\https://www.microsoft.com/json-in-sql-server-2016-part-3-of-4\/","title":{"rendered":"JSON in SQL Server 2016: Part 3 of 4"},"content":{"rendered":"
OPENJSON is a table-value function (TVF) that looks into JSON text, locates an array of JSON objects, iterates through the elements of the array, and for each element returns one row in the output result.<\/p>\n
<\/a><\/p>\n In the example above, we can specify where to locate the JSON array that should be opened (i.e. in the $.Orders path), what column should be returned as result, and where in the JSON objects values can be located that will be returned as cells.<\/p>\n OPENJSON can be used in any query that works with data. As an example, we can transform a JSON array in the @orders variable into a set of rows and insert them into a standard table:<\/p>\n INSERT INTO<\/span> Orders(<\/span>Number,<\/span> Date<\/span>,<\/span> Customer,<\/span> Quantity)<\/span><\/span> Four columns in the result set that is returned by OPENJSON are defined in the WITH clause. OPENJSON will try to find the properties Number, Date, Customer, and Quantity in each JSON object and convert their values into columns in the result set. By default, NULL will be returned if the property is not found. The assumption in the query above is that the @orders variable contains the following JSON array:<\/p>\n ‘[<\/span> As you can see, the transformation from a JSON text to a relational form is simple. You just need to specify column names and types and OPENJSON will find properties in JSON that match these columns. In this example, plain JSON is used; however, OPENJSON can handle any nested\/hierarchical structure of JSON objects.<\/p>\n Also, OPENJSON can be used to combine relational and JSON data in the same query. If we assume that the JSON array shown in the previous example is stored in the Orders column, the following query can combine the columns and JSON fields:<\/p>\n SELECT<\/span> Id,<\/span> FirstName,<\/span> LastName,<\/span> Number,<\/span> Date<\/span>,<\/span> Customer,<\/span> Quantity<\/span> OPENJSON will open an array in each cell and return one row for each JSON object (i.e. element) in the array. CROSS APPLY OPENJSON syntax is used to \u201cjoin\u201d rows in the table with the child inner table that will be materialized from a JSON array in the JSON cell.<\/p>\n Although values in JSON are formatted as text, you can index them the same way as any other values in table columns. You can use either standard NON CLUSTERED or full text search indexes.<\/p>\n If you want to create an index on some JSON property that is frequently used in queries, you can create a non-persisted computed column that references the value and creates a standard index on that column. In the following example, we will optimize queries that filter rows using the $.Company property in the InfoJSON column:<\/p>\n ALTER TABLE<\/span> Person<\/span> CREATE INDEX<\/span> idx_Person_1<\/span> As you can see, SQL Server provides a hybrid model where you can put values from JSON either in key or included columns and uses both JSON values and standard columns in the same index.<\/p>\n Since JSON is regular text, you can use standard full text index. Full text indexes can be created on arrays of values. You create a full text index on a column that contains a JSON array, or you can create a computed column that references some array in the existing column and create a full text search index on that column:<\/p>\n ALTER TABLE<\/span> Person<\/span> CREATE FULLTEXT INDEX ON<\/span> Person(<\/span>vEmailAddresses)<\/span><\/span> Full text index is useful if you need to optimize queries that try to find rows where the JSON array contains some value:<\/p>\n SELECT<\/span> PersonID,<\/span> FirstName,<\/span>LastName,<\/span>vEmailAddresses<\/span> This query will return Person rows where the email array contains the value \u2018john@mail.microsoft.com.\u2019 Full text index doesn\u2019t have any special rules for parsing JSON. It will split a JSON array using separators (i.e. double quotes, commas, brackets) and index values in an array. Full text index is applicable on arrays of numbers and simple string values. If you have more complex objects in a JSON array, a full text index cannot be directly applied because the system does not know the difference between keys and values.<\/p>\n As you can see, the same indexing methods are used both on JSON values and relational columns.<\/p>\n Check out the other posts in this four-part series in the links below (as they become available), or\u00a0learn more in the SQL Server 2016 blogging series<\/a>.<\/p>\n JSON in SQL Server 2016: Part 1 of 4<\/a><\/p>\n JSON in SQL Server 2016: Part 2 of 4<\/a><\/p>\n JSON in SQL Server 2016: Part\u00a04 of 4<\/a><\/p>\n
SELECT<\/span> Number,<\/span> Date<\/span>,<\/span> Customer,<\/span> Quantity<\/span>
\u00a0OPENJSON<\/span> (<\/span>@orders)<\/span><\/span>
\u00a0WITH<\/span> (<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Number varchar<\/span>(<\/span>200),<\/span> <\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Date datetime<\/span>,<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Customer varchar<\/span>(<\/span>200),<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Quantity int<\/span><\/span>
\u00a0)<\/span> AS<\/span> OrdersArray<\/span><\/p>\n
\u00a0\u00a0 {“Number”:1, “Date”: “8\/10\/2012”, “Customer”: “Adventure works”, “Quantity”: 1200},<\/span>
\u00a0\u00a0 {“Number”:4, “Date”: “5\/11\/2012”, “Customer”: “Adventure works”, “Quantity”: 100},<\/span>
\u00a0\u00a0 {“Number”:6, “Date”: “1\/3\/2012”, “Customer”: “Adventure works”, “Quantity”: 250},<\/span>
\u00a0\u00a0 {“Number”:8, “Date”: “12\/7\/2012”, “Customer”: “Adventure works”, “Quantity”: 2200}<\/span>
]’<\/span><\/p>\n
\u00a0FROM<\/span> Person<\/span>
\u00a0\u00a0\u00a0 CROSS APPLY<\/span> OPENJSON<\/span> (<\/span>OrdersJson)<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 WITH<\/span> (<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Number varchar<\/span>(<\/span>200), <\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Date datetime<\/span>,<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Customer varchar<\/span>(<\/span>200),<\/span><\/span>
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Quantity int<\/span> )<\/span> AS<\/span> OrdersArray<\/span><\/p>\nIndexing JSON data<\/h1>\n
ADD<\/span> vCompany AS<\/span> JSON_VALUE<\/span>(<\/span>InfoJSON,<\/span> ‘<\/span>$.Company’<\/span>)<\/span><\/span><\/p>\n
\u00a0\u00a0\u00a0 ON<\/span> Person(<\/span>vCompany)<\/span><\/span><\/p>\n
ADD<\/span> vEmailAddresses AS<\/span> JSON_QUERY<\/span>(<\/span>InfoJSON,<\/span> ‘$.Contact.Emails’)<\/span><\/span><\/p>\n
\u00a0\u00a0\u00a0 KEY INDEX<\/span> PK_Person_ID ON<\/span> jsonFullTextCatalog;<\/span><\/p>\n
FROM<\/span> Person<\/span>
WHERE<\/span> CONTAINS<\/span>(<\/span>vEmailAddresses,<\/span> ‘john@mail.microsoft.com’<\/span>)<\/span><\/span><\/p>\n