0

I have a table with an VARCHAR(MAX) column which stores JSON key value pairs. The JSON document schema is simply a different number of key value pairs, no nesting, nor arrays in there.

I wish to build a query, which gives back the JSON in a tabular format, which is easy with the named elements (see WITH clause below):

DECLARE @MYJSONTABLE TABLE (ID INT IDENTITY NOT NULL PRIMARY KEY, MYDATA NVARCHAR(max) null)
INSERT INTO @MYJSONTABLE
(
    MYDATA
)
VALUES
(N'{"id": 2, "info": "some info", "age": 25}'),
(N'{"id": 5, "info": "other info", "dob": "2005-11-04T12:00:00"}')

SELECT p.ID,MYDATA.*
FROM @MYJSONTABLE p
CROSS APPLY 
OPENJSON(p.MYDATA)
WITH (
    id INT 'strict $.id',
    info NVARCHAR(50) '$.info',
    age INT,
    dateOfBirth DATETIME2 '$.dob'
) AS MYDATA

While the output is exactly what I want, my issue with the above solution, that I don't know the key names in the JSON document neither, and how many are there, but still wish to return them all in the same tabular format.

If I omit the WITH clause above, the query do return all key value pairs, but the output goes "vertical" and each key in the JSON generates a new row.

Could the above query be modified to be dynamic, and return all key value pairs without explicitly specifying the JSON key names?

2
  • 1
    If you did do this dynamically, you would have to assume all of your data was an (n)varchar; unless you have some kind of look up table that tells you the data types of the elements?
    – Thom A
    Commented Jan 11, 2022 at 10:35
  • Right, its ok to me to have them all returned as nvarchar.
    – Avi
    Commented Jan 11, 2022 at 10:42

1 Answer 1

2

Perhaps something like this will work for you.

This uses a CTE to get the DISTINCT key's from your JSON. Then string aggregation to create a dynamic statement, which you can see from the PRINT statement.

Note that for your sample data, the column dob is not returned because it is outside of the initial JSON defined. If the first right brace (}) is removed, the column appears.

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10);

DECLARE @Delimiter nvarchar(50) = N',' + @CRLF + N'                      ';

WITH Keys AS(
    SELECT DISTINCT J.[key]
    FROM dbo.YourTable YT
         CROSS APPLY OPENJSON(YT.JsonColumn) J)
SELECT @SQL = N'SELECT YT.ID,' + @CRLF +
              N'       J.*' + @CRLF +
              N'FROM dbo.YourTable YT' + @CRLF +
              N'     CROSS APPLY OPENJSON(YT.JsonColumn)' + @CRLF +
              N'                 WITH(' +
              STRING_AGG(QUOTENAME(K.[key]) + N' nvarchar(100)', @Delimiter) + N') J;'
FROM Keys K;

PRINT @SQL;
EXEC sys.sp_executesql @SQL;

Note, this will not work with a table variable, unless you create a table type and then pass the TYPE as a parameter to sys.sp_executesql. This is why the above assumes a real table.

3
  • That's works! I hoped there is built in function, or secret parameter to openjson that enlist all the available keys. This dynamic sql is cumbersome to work with.
    – Avi
    Commented Jan 13, 2022 at 19:52
  • You're welcome @avi . If it answers the question please.do accept it as the solution so that future readers know the answer was helpful.
    – Thom A
    Commented Jan 13, 2022 at 19:55
  • 1
    In regards to needing to use dynamic SQL, @Avi ,despite what people think T-SQL is a compiled language, and a dataset needs to be well defined before it is run. As such you can't just say (for lack of better words) "give me all the data from the JSON" as the data engine doesn't know what those columns are, nor what data types they should be; it needs to be told so it can compile the needed statement(s).
    – Thom A
    Commented Jan 13, 2022 at 19:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.