Practical .NET

Retrieving and Updating JSON Objects in SQL Server 2016

With SQL Server 2016, you can store JSON objects in your rows. Here’s how to work with JSON objects, including how to update them once you’ve found them.

In a previous Practical .NET column I showed how to retrieve rows that hold JSON objects by querying on values in those objects’ properties. However, sometimes you’ll want to work with the complete JSON object rather than with individual values. SQL Server supports that, too … including updating those object’s properties once you retrieve them.

Working with Arrays
For example, you might have an array of JSON stored in an NVarChar column, rather than an individual object as I discussed in my previous column. That array might look like this:

[{"Name":{"First": "Peter", 
 	  "Last": "Vogel"}, 
 "IsPremium": true, 
 "SignupDate":"2017-09-30"},
{"Name":{"First": "Jan", 
 	  "Last": "Irvine"}, 
 "IsPremium": false, 
 "SignupDate":"2015-01-14"}]

The JSON path expressions I discussed in my previous column allow you to refer to an object in the array using a zero-based index enclosed in brackets ([]). A query to retrieve the first name of the second item in my sample array would look like this:

Select JSON_VALUE(LastUpdate, '$[1].Name.First') AS FirstName
From Customers

Using the array syntax to access a column that doesn’t contain an array or to retrieve a position in the array that doesn’t exist will return either NULL or an error, depending on whether you’re in lax or strict mode (the default is lax so my previous query would return NULL if either the Name or First properties don’t exist). This query would generate an error because I’m using strict mode and there’s no object at the 10th position in the array:

Select JSON_VALUE(LastUpdate, 'strict $[10].Name.First') AS FirstName
From Customers

Retrieving JSON Objects
If what you’re interested in retrieving is a complete JSON object, you can retrieve JSON fragments using the JSON_QUERY function. For these examples, I’ll assume I have a LastUpdate column in my row containing this JSON object:

{"Name":{"First": "Peter", 
 	  "Last": "Vogel"}, 
 "IsPremium": true, 
 "SignupDate":"2017-09-30"}

This query returns the JSON fragment in my JSON object’s Name property:

Select JSON_QUERY(LastUpdate, '$.Name')
From Customers

The result is this string:

{"First": "Peter", "Last": "Vogel"}

Unlike JSON_VALUE, which requires a path expression, with JSON_QUERY you can omit the path expression to retrieve the entire contents of the column. The result, though, is the equivalent of just referencing the column. These two queries, for example, return identical results:

Select JSON_QUERY(LastUpdate) As JSON
From Customers

Select LastUpdate As JSON
From Customers

Strings vs. Objects
JSON_QUERY doesn’t actually return a JSON object -- the function is only returning a string. Using JSON_QUERY does, however, prevent the string representation of your JSON object from being escaped (that is, having backslashes inserted in front of each of your double quotes). Because JSON_QUERY returns a string, using JSON_QUERY in a Where clause is … challenging: The JSON object you might be comparing has to be a character-to-character match to the JSON fragment that JSON_QUERY returns.

The following query, for example, will only work if I’m very careful with the contents of the string on the right side of the equals sign (=). For example, inserting a stray blank space around either of the colons (:) would cause the comparison to fail:

Select JSON_QUERY(LastUpdate, '$.Name') As JSON
From Customers
Where JSON_QUERY(LastUpdate, '$.Name') = '{"First": "Peter", "Last": "Vogel"}'

You can pass the output of a JSON_QUERY to the OPENJSON function to have the fragment converted to a virtual table, effectively creating a table from a column in the row. You can then join that virtual table to its row using CROSS APPLY. This query, for example, joins the JSON objects in the LastUpdate column to its table and, based on the customer’s last name, finds those rows where the name in the row and the name in the JSON object don’t match:

Select c.CustomerId
From Customers As c
  
  Cross Apply OPENJSON(JSON_QUERY(LastUpdate, '$.Name')) WITH (Last nvarchar(20)) AS JSONTable
Where JSONTable.Last <> c.LastName

Of course, that result could also be achieved with this simpler query:

SELECT CustomerId
FROM Customers
WHERE (JSON_VALUE(LastUpdate, '$.Name.Last') <> LastName)

Updating JSON
If you want to retrieve a JSON object so you can change a value in one of its properties, JSON_MODIFY lets you do that. You must pass the JSON_MODIFY function the JSON object, a JSON path expression to the property you want to change and the value to be inserted into the property. The function returns the updated JSON object, which, in an Update statement, you can use to change the value of your JSON column.

This query sets the IsPremium property of every Customer’s JSON object in the LastUpdate column to false while updating the row’s LastUpdate column with the changed JSON object:

Update Customers
Set LastUpdate = JSON_MODIFY(LastUpdate, '$.IsPremium', 'false')

You can also add a new object to JSON arrays by adding the "append" keyword in front of your path. This query adds a new value to the SignupDates array:

Update Customers
Set LastUpdate = JSON_MODIFY(LastUpdate, 'append $.SignupDates', '2017-02-28')

JSON’s "flexibility" (for want of a better word) can trip you up here. If the JSON object’s SignupDates property doesn’t contain an array, nothing will be added; If the JSON object doesn’t have a SignupDates property, then one will be added, holding an array with one value in it. Because my JSON object doesn’t have a SignupDates property, my previous update statement will give me this:

{"Name":{"First": "Peter", 
 	  "Last": "Vogel"}, 
 "IsPremium": true, 
 "SignupDate":"2017-09-30",
 "SignupDates":["2017-02-28"]}

As that discussion suggests, you can also use append to add a new property to a JSON object.

As AJAX becomes more prevalent, JSON is only going to become more important. Armed with the tools I’ve discussed in this and previous columns, you can treat your JSON data as JSON data, even in your relational database.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events