Search code examples
jsonsql-servert-sqlsql-server-2016for-json

Combine JSON objects from multiple rows into a single object


CREATE TABLE t(Id int, typee nvarchar(50), jsonStr nvarchar(max));
INSERT INTO t(Id, typee, jsonStr) VALUES
(3786, 'APV', '{"1":1,"3":3,"4":24,"5":95}'),
(3786, 'VN', '{"1":3,"5":25}');

-- Expected result
-- {"APV": {"1":1,"3":3,"4":24,"5":95}, "VN":{"1":3,"5":25} }

SELECT Id,(
    SELECT CASE WHEN typee = 'VN'  THEN jsonStr END AS [VN]
         , CASE WHEN typee = 'VO'  THEN jsonStr END AS [VO]
         , CASE WHEN typee = 'APV' THEN jsonStr END AS [APV]
    FROM t AS x
    WHERE x.Id = t.Id
    FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER
) AS TEST1

FROM t
GROUP BY Id

DB<>Fiddle

I would like to get the outuput like:

{
  "APV": {
    "1": 1,
    "3": 3,
    "4": 24,
    "5": 95
  },
  "VN": {
    "1": 3,
    "5": 25
  }
}

Solution

  • The problem is that you are storing JSON and then formatting returning it further as JSON. You would need to store non-JSON data to do it the way you want. Therefore it seems easier to treat it as a string:

    SELECT t.id,
           '{' + STRING_AGG( '"' +t.typee + '": ' + t.jsonStr,',') WITHIN GROUP (ORDER BY typee) + '}'
    FROM t
    GROUP BY t.id;
    

    Using FOR XML PATH:

    SELECT t1.id,
           '{' + STUFF((SELECT ',"' + t2.typee + '": ' + t2.jsonStr
                        FROM t t2
                        WHERE t2.Id = t1.id
                        FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,1,'') + '}'
    FROM t t1
    GROUP BY t1.id;