Previously I had blogged on how JSON documents can be generated from relational data using FOR JSON construct here
http://visakhm.blogspot.ae/2016/06/whats-new-in-sql-2016-native-json.html
and also how JSON documents can be parsed and relational data can shred from them using OPENJSON here
http://visakhm.blogspot.ae/2016/07/whats-new-in-sql-2016-native-json.html
The article explains on some of the JSON functions which is available in Transact SQL and can be utilized for manipulating the data within JSON documents.
ISJSON()
declare @x nvarchar(max)='{
“Items”: [
{
“ItemID”: 101,
“ItemDesc”: “Monitor CRT 18″”,
“Unitprice”: 200
},
{
“ItemID”: 110,
“ItemDesc”: “Printer Catridge”,
“Unitprice”: 112
},
{
“ItemID”: 112,
“ItemDesc”: “Copier Ink”,
“Unitprice”: 20
},
{
“ItemID”: 123,
“ItemDesc”: “Wireless Mouse”,
“Unitprice”: 30
}
]
}’
SELECT ISJSON(@x)
The result will be 1 as @x contains valid JSON data
If at all the passed document can be considered as a valid JSON, ISJSON function returns 1 as seen from the below examples
SELECT *
FROM
(
VALUES (‘{}’),(‘[]’),(‘{“item”:1}’),(‘{“q”:[1]}’),(‘[0,1,2]’)
)t(v)
WHERE ISJSON(v)=1
and the result
The first two values represents blank JSON object and array documents whereas the next three represents JSON document with single object/array object and all of them are valid.
One more thing you may note from the above illustration is how we can use ISJSON function as a filter condition in the WHERE clause to check for the rows with valid JSON data in the column and then to do some manipulations on them.
JSON_VALUE()
Lets see a simple example of how JSON_VALUE can be applied to get values from a JSON document
declare @x nvarchar(max)='{
“Item”:
{
“ItemID”: 101,
“ItemDesc”: “Monitor CRT 18″”,
“Unitprice”: 200
}
}’
The above shows a very simple JSON document with a single JSON element having three key value pairs.
The values from the above JSON document can be parsed out using JSON_VALUE function as per below code
SELECT JSON_VALUE(@x,’$.Item.ItemID’) AS ItemID,
JSON_VALUE(@x,’$.Item.ItemDesc’) AS ItemDesc,
JSON_VALUE(@x,’$.Item.Unitprice’) AS Unitprice
The result is as below
This is a simple document with all key values in the same level. Now lets see how we can use JSON_VALUE in the case where JSON document has key values in multiple levels
Consider the below JSON example
declare @x nvarchar(max) = ‘{
“Order”: {
“OrderDesc”: “Order 1”,
“OrderDate”: “2013-02-12T00:00:00”,
“ShippedDate”: “2013-02-20T00:00:00”,
“WebOrder”: false,
“OrderItems”:
{
“OrderQty”: 10,
“Item”: {
“ItemDesc”: “Wireless Mouse”,
“Unitprice”: 30,
“SUbItem”: {
“SubItemDesc”: “SP17”
}
}
}
}’
Now to get the data from the various levels within the document we can apply JSON_VALUE function as per below
SELECT JSON_VALUE(@x,’$.Order.OrderDesc’) AS OrderDesc,
JSON_VALUE(@x,’$.Order.OrderDate’) AS OrderDate,
JSON_VALUE(@x,’$.Order.ShippedDate’) AS ShippedDate,
JSON_VALUE(@x,’$.Order.WebOrder’) AS WebOrder,
JSON_VALUE(@x,’$.Order.OrderItems.OrderQty’) AS OrderQty,
JSON_VALUE(@x,’$.Order.OrderItems.Item.ItemDesc’) AS ItemDesc,
JSON_VALUE(@x,’$.Order.OrderItems.Item.Unitprice’) AS Unitprice,
JSON_VALUE(@x,’$.Order.OrderItems.Item.SUbItem.SubItemDesc’) AS SubItemDesc
One thing to note here is that JSON_VALUE can return only a scalar value from within a single key value pair in the JSON data. So in case you’re referring to a JSON array object which includes multiple key value pairs as per the path specified it will return NULL under the default mode.
This can be illustrated using the below example
declare @x nvarchar(max) = ‘{
“Order”:
{
“OrderDesc”: “Order 1”,
“OrderDate”: “2013-02-12T00:00:00”,
“ShippedDate”: “2013-02-20T00:00:00”,
“WebOrder”: false,
“OrderItems”:
{
“OrderQty”: 10,
“Item”: [
{
“ItemDesc”: “Wireless Mouse”,
“Unitprice”: 30,
“SUbItem”: [
{
“SubItemDesc”: “SP17”
},
{
“SubItemDesc”: “SP22”
}
]
},
{
“ItemDesc”: “Monitor”,
“Unitprice”: 22,
“SUbItem”: [
{
“SubItemDesc”: “SP27”
}
]
}
]
}
}
}’
SELECT JSON_VALUE(@x,’$.Order.OrderDesc’) AS OrderDesc,
JSON_VALUE(@x,’$.Order.OrderDate’) AS OrderDate,
JSON_VALUE(@x,’$.Order.ShippedDate’) AS ShippedDate,
JSON_VALUE(@x,’$.Order.WebOrder’) AS WebOrder,
JSON_VALUE(@x,’$.Order.OrderItems.OrderQty’) AS OrderQty,
JSON_VALUE(@x,’$.Order.OrderItems.Item.ItemDesc’) AS ItemDesc,
JSON_VALUE(@x,’$.Order.OrderItems.Item.Unitprice’) AS Unitprice,
JSON_VALUE(@x,’$.Order.OrderItems.Item.SUbItem.SubItemDesc’) AS SubItemDesc
If you try the above illustration the result obtained will be something like below
Analyze the result and you will find that the code is not able to retrieve any keys at the Item and SUbtem level using JSON_VALUE function.
If you check the JSON document carefully you can see that Item and SUBItem consists of actually an array object (notice the [] around them) rather than a single JSON element. This is why JSON_VALUE cant extract value from them. As previously explained the JSON_VALUE can only extract a single scalar value which is why it fails in the above scenario.
For the above case if we want to get the values we need to tweak the query as below to get the Item and SUbItem level key values
The modified query would be like this
SELECT JSON_VALUE(@x,’$.Order.OrderDesc’) AS OrderDesc,
JSON_VALUE(@x,’$.Order.OrderDate’) AS OrderDate,
JSON_VALUE(@x,’$.Order.ShippedDate’) AS ShippedDate,
JSON_VALUE(@x,’$.Order.WebOrder’) AS WebOrder,
JSON_VALUE(@x,’$.Order.OrderItems.OrderQty’) AS OrderQty,
JSON_VALUE(@x,’$.Order.OrderItems.Item[0].ItemDesc’) AS ItemDesc1,
JSON_VALUE(@x,’$.Order.OrderItems.Item[0].Unitprice’) AS Unitprice1,
JSON_VALUE(@x,’$.Order.OrderItems.Item[0].SUbItem[0].SubItemDesc’) AS SubItemDesc11,
JSON_VALUE(@x,’$.Order.OrderItems.Item[0].SUbItem[1].SubItemDesc’) AS SubItemDesc12,
JSON_VALUE(@x,’$.Order.OrderItems.Item[1].ItemDesc’) AS ItemDesc2,
JSON_VALUE(@x,’$.Order.OrderItems.Item[1].Unitprice’) AS Unitprice2,
JSON_VALUE(@x,’$.Order.OrderItems.Item[1].SUbItem[0].SubItemDesc’) AS SubItemDesc21
And the result below
But this method is not scalable as we have to be certain on the number of elements at query time to apply the position number based static logic. In such cases we would require logic which will iterate through the JSON elements to return corresponding key values. This will be explained in detail later in this article.
strict and lax modes
In contrast strict mode the JSON path passed is strictly checked against and in case it is an invalid path it returns an error.
To illustrate this lets use a modified version of the last query in the two modes and see the difference
lax mode
SELECT JSON_VALUE(@x,’lax $.Order.OrderDesc’) AS OrderDesc,
JSON_VALUE(@x,’lax $.Order.OrderDate’) AS OrderDate,
JSON_VALUE(@x,’lax $.Order.ShippedDate’) AS ShippedDate,
JSON_VALUE(@x,’lax $.Order.WebOrder’) AS WebOrder,
JSON_VALUE(@x,’lax $.Order.OrderItems.OrderQty’) AS OrderQty,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[0].ItemDesc’) AS ItemDesc1,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[0].Unitprice’) AS Unitprice1,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[0].SUbItem[0].SubItemDesc’) AS SubItemDesc11,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[0].SUbItem[1].SubItemDesc’) AS SubItemDesc12
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[1].ItemDesc’) AS ItemDesc2,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[1].Unitprice’) AS Unitprice2,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[1].SUbItem[0].SubItemDesc’) AS SubItemDesc21,
JSON_VALUE(@x,’lax $.Order.OrderItems.Item[1].SUbItem[1].SubItemDesc’) AS SubItemDesc22
and the result
Notice the NULL value for the nonexistent key coming at the end. This query works without any error because of lax mode
Now lets see what happens with the same query in the strict mode
SELECT JSON_VALUE(@x,’strict $.Order.OrderDesc’) AS OrderDesc,
JSON_VALUE(@x,’strict $.Order.OrderDate’) AS OrderDate,
JSON_VALUE(@x,’strict $.Order.ShippedDate’) AS ShippedDate,
JSON_VALUE(@x,’strict $.Order.WebOrder’) AS WebOrder,
JSON_VALUE(@x,’strict $.Order.OrderItems.OrderQty’) AS OrderQty,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[0].ItemDesc’) AS ItemDesc1,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[0].Unitprice’) AS Unitprice1,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[0].SUbItem[0].SubItemDesc’) AS SubItemDesc11,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[0].SUbItem[1].SubItemDesc’) AS SubItemDesc12,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[1].ItemDesc’) AS ItemDesc2,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[1].Unitprice’) AS Unitprice2,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[1].SUbItem[0].SubItemDesc’) AS SubItemDesc21,
JSON_VALUE(@x,’strict $.Order.OrderItems.Item[1].SUbItem[1].SubItemDesc’) AS SubItemDesc22
Now the result
JSON_QUERY()
Parsing JSON Array Objects And Nested Levels
JSON_MODIFY()
1. Create a new key to JSON element
“Order”:{
“OrderDesc”:“Order 1”,
“OrderDate”:“2013-02-12T00:00:00”,
“ShippedDate”:“2013-02-20T00:00:00”,
“WebOrder”:false,
“OrderItems”:{
“OrderQty”:10,
“Item”:{
“ItemDesc”:“Wireless Mouse”,
“Unitprice”:“30”,
“SUbItem”:{
“SubItemDesc”:“SP17”
},
“ItemQty”:50
}
}
}
}
2. Append a value to JSON element
“Order”:{
“OrderDesc”:“Order 1”,
“OrderDate”:“2013-02-12T00:00:00”,
“ShippedDate”:“2013-02-20T00:00:00”,
“WebOrder”:false,
“OrderItems”:{
“OrderQty”:10,
“Item”:{
“ItemDesc”:[
“Wireless Mouse”,
“Optical USB Enabled”
],
“Unitprice”:“30”,
“SUbItem”:{
“SubItemDesc”:“SP17”
}
}
}
}
}
3. Rename a key within JSON document
“Order”:{
“OrderDesc”:“Order 1”,
“OrderDate”:“2013-02-12T00:00:00”,
“ShippedDate”:“2013-02-20T00:00:00”,
“WebOrder”:false,
“OrderItems”:{
“OrderQty”:10,
“Item”:{
“ItemDesc”:[
“Wireless Mouse”
],
“Unitprice”:“30”,
“SubItem”:{
“SubItemDesc”:“SP17”
}
}
}
}
Analyse the result and you can see that the JSON document key element has been renamed from SUbitem to SubItem.
4. Delete a key within JSON document
The code is as given below
SET @x = JSON_MODIFY(@x,’$.Order.OrderItems.Item.ItemQty’,NULL)
SELECT @x
The result will look like this
{
“Order”:{
“OrderDesc”:“Order 1”,
“OrderDate”:“2013-02-12T00:00:00”,
“ShippedDate”:“2013-02-20T00:00:00”,
“WebOrder”:false,
“OrderItems”:{
“OrderQty”:10,
“Item”:{
“ItemDesc”:[
“Wireless Mouse”
],
“Unitprice”:“30”,
“SubItem”:{
“SubItemDesc”:“SP17”
}
}
}
}
}
If you notice the result you will see that the ItemQty key is missing in the document which indicates that key has been deleted from the document.
Please note that all the above queries are using the default lax mode. While using strict mode it will throw the standard error indicating JSON path not found if you pass a non existing key name.