SQL Server OPENJSON: Parse Hierarchical JSON into Relational Tables

Comments 0

Share to social media

OPENJSON is SQL Server’s function for parsing JSON strings into relational table structures. At its simplest, it converts a JSON array or object into a table-valued result you can query with standard SQL. For hierarchical JSON – nested objects and arrays – OPENJSON requires recursive or iterative processing to flatten the structure into a relational form. This article covers three patterns: using OPENJSON for simple list and EAV parsing; a recursive multi-line table-valued function for full hierarchy extraction; and an iterative version that is significantly faster. OPENJSON requires database compatibility level 130 (SQL Server 2016) or higher.

Articles by Phil Factor about JSON and SQL Server:

  1. Consuming JSON Strings in SQL Server (Nov 2012)
  2. SQL Server JSON to Table and Table to JSON (March 2013)
  3. Producing JSON Documents From SQL Server Queries via TSQL (May 2014)
  4. Consuming hierarchical JSON documents in SQL Server using OpenJSON (Sept 2017)
  5. Importing JSON data from Web Services and Applications into SQL Server(October 2017)

JSON isn’t the easiest of document formats for transferring tabular data, but it is popular and you are likely to need to use it. This used to be a big problem in SQL Server because there wasn’t a native method for either creating or consuming JSON documents until SQL Server 2016. I wrote a succession of articles, Consuming JSON Strings in SQL Server, Producing JSON Documents From SQL Server Queries via TSQL and SQL Server JSON to Table and Table to JSON that illustrated ways of doing it, slow and quirky though they were. They used a simple adjacency list table to store the denormalised hierarchical information so it could be shredded into a relational format. This table stores sufficient information that you could, if you really wanted, create an XML file from it that loses nothing from the translation.

I wrote these articles before SQL Server adopted JSON in SQL Server 2016. Then, it was difficult. Actually it is still hardly plain sailing, but there are some excellent articles on the Microsoft site to explain it all.

The OpenJSON() function is, at its simplest, a handy device for  representing  small lists or EAV table sources as strings. For example you can pass in a simple list (roman numerals in this case) …

… and get this.

You can pass in an EAV list, with both keys and values…

… which produces … 

But if you don’t like this format you can have a  traditional table source.

This gives the result..

As you can imagine, OpenJSON’s use with lists is useful where you have to deal with a variable number of parameters. In this example, we pass a list of object IDs to an inline Table-valued function  to get back  a table with the object’s full ‘dotted’ name.

This is just scratching the surface, of course. In this article OpenJSON is destined for greater and more complicated usage to deal with the cases where the JSON is hierarchical.

I was asked the other day how to use OpenJSON to parse JSON into a hierarchy table like the one I used. The most pressing thing to do was to make a sensible substitute for the rather obtuse ParseJSON() function by using OpenJSON() instead.

To get OpenJSON to work, you will need to be at the right compatibility level. This code will, if you change the ‘MyDatabase’ to the name of your database, and have the right permissions, set the correct compatibility level.

Firstly, we will need to define a user-defined table type that can be used as an input variable for functions.

Now we can go ahead and create the actual function. First we will try a  recursive multi-line table-valued function. Note that there is only one parameter you need, which is the string containing the JSON. The other three parameters are only used when the function is being called recursively. You need to just use the DEFAULT keyword for these other parameters.

And we can now test it out

This will produce this result

The surprising fact from putting it in a test harness with some more weighty JSON is that it is slightly slower (10%) than the old function that I published in the dark days of SQL Server 2008. This is because recursion of a scalar function is allowed, but it is slow. I went on to test out a similar recursive function that did the conversion back to JSON from a hierarchy table, and that was seven times slower.  Although recursive scalar functions and CTEs are easy to write, they aren’t good for performance.

We can test out our new function by converting back into XML

Now we can check that everything is there from the contents of the @xml variable.

To capture the entire hierarchy, we had to call OpenJSON recursively.

You can avoid recursion entirely with SQL, and almost always this is an excellent idea. Here is an iterative version of the task.  It looks less elegant than the recursive version but runs a five times the speed.  a seventy row JSON document is translated into a hierarchy table in 25 ms on my very slow test server!

…and a quick test run confirms that this version is much faster, and a great improvement on the old parser from the dark days before OpenJSON

Usually, OpenJSON is easier to use. If the JSON document represents a simple table, we can be distinctly relaxed.  The OpenJSON routine is excellent for the chore of turning JSON-based tables into results. There was a time that this was much more awkward (see this article)

Which gives …

SQL Server’s JSON support is good and solid. It makes life easier for conversion but it is not as slick as SQL Server’s XML support. It is certainly a lot quicker and more effective than was possible before SQL Server 2016.

FAQs: Consuming hierarchical JSON documents in SQL Server using OpenJSON

1. What does SQL Server OPENJSON do?

OPENJSON is a table-valued function available from SQL Server 2016 (compatibility level 130) that parses a JSON string and returns each JSON value as a row. It can return the raw key-value-type structure of any JSON, or you can specify a WITH clause to define a fixed schema for the output table. It is the primary tool for converting JSON strings into relational rows you can JOIN, filter, and aggregate.

2. How do I convert hierarchical JSON to a table in SQL Server?

For flat JSON arrays, use OPENJSON with a WITH clause defining the output columns. For hierarchical JSON with nested objects and arrays, call OPENJSON recursively – passing nested JSON paths into subsequent OPENJSON calls. For better performance on large documents, use the iterative approach described in this article, which avoids SQL recursion limits and processes significantly faster than the recursive multi-line table-valued function.

3. Does OPENJSON work on all SQL Server versions?

OPENJSON requires database compatibility level 130 or higher, which corresponds to SQL Server 2016. Run ALTER DATABASE [YourDB] SET COMPATIBILITY_LEVEL = 130 to enable it. On SQL Server 2014 and earlier, OPENJSON is not available – you need a custom CLR function or T-SQL JSON parser. On Azure SQL Database and SQL Server 2019+, the required compatibility level is met by default.

4. What is the difference between OPENJSON and JSON_VALUE in SQL Server?

JSON_VALUE extracts a single scalar value from a JSON string using a path expression – for example, JSON_VALUE(@json, ‘$.name’) returns ‘Phil’. OPENJSON converts a JSON string into a table with multiple rows and columns. Use JSON_VALUE when you need one specific field from a known JSON structure; use OPENJSON when you need to process multiple values, iterate over an array, or handle variable or unknown JSON schemas.

Article tags

About the author

Phil Factor

See Profile

Phil Factor (real name withheld to protect the guilty), aka Database Mole, has 40 years of experience with database-intensive applications. Despite having once been shouted at by a furious Bill Gates at an exhibition in the early 1980s, he has remained resolutely anonymous throughout his career. See also :