SQL Server:使用 group by 查询列到 JSON 对象

问题描述 投票:0回答:6

我有一个包含 3 列的表,我想查询该表,结果将是一个 JSON 对象。

示例数据如下所示:

 CREATE TABLE #Test (ValueV INT, KEYS NVARCHAR (100), ID INT) 

 INSERT INTO #Test 
 VALUES (1, N'ChangeAdress 19 - 21', 200),
        (1, N'ChangeAdress 20 - 22', 200),
        (1, N'ChangeAdress 22 - 24', 300),
        (1, N'ChangeAdress 23 - 25', 300),
        (2, N'ChangeAdress 24 - 26', 400),
        (2, N'ChangeAdress 25 - 27', 400),
        (3, N'ChangeAdress 26 - 28', 400),
        (3, N'ChangeAdress 27 - 29', 400)

 SELECT * FROM #Test

我的查询尝试:

 SELECT ID, Keys, ValueV
 FROM #Test  
 GROUP BY ID, keys, ValueV
 FOR JSON AUTO

但这会返回 1 个 JSON“行”。我想要的是每组一行。这里的Group就是ID,Value的组合。我对 JSON 对象缺乏经验(这可能从此查询中可见),因此我们将不胜感激。

所需的输出(但随后为每行 JSON):

 --------------------------------------------------
|200, 1, ChangeAdress 19 - 21, ChangeAdress 20 - 22|
|300, 1, ChangeAdress 22 - 24, ChangeAdress 23 - 25|
|400, 2, ChangeAdress 24 - 26, ChangeAdress 25 - 27|
|400, 3, ChangeAdress 26 - 28, ChangeAdress 27 - 29|

提前致谢!

sql json sql-server t-sql
6个回答
10
投票

这可行(在 SQL Server 2017 中,

STRING_AGG
可用),但相当笨拙。我不确定还有没有更优雅的方法。

SELECT (
    SELECT 
       ID, 
       ValueV, 
       Keys = JSON_QUERY('["' + STRING_AGG(STRING_ESCAPE(Keys, 'json'), '","') + '"]')
    FOR JSON PATH
)
FROM #Test 
GROUP BY ID, ValueV

对于 SQL Server 2016(没有

STRING_AGG
STRING_ESCAPE
):

SELECT (
    SELECT ID, ValueV, Keys = JSON_QUERY(REPLACE(REPLACE(
        (
            SELECT Keys 
            FROM #Test t2 WHERE t2.ID = t1.ID AND t2.ValueV = t1.ValueV 
            FOR JSON PATH
        ),
        '{"Keys":', ''),
        '}', ''))
    FOR JSON PATH
)
FROM #Test t1
GROUP BY ID, ValueV

更不优雅,但你能得到什么就拿什么。至少我们没有连接

FOR XML
...


7
投票

如果有人有相同的用例,请发布此内容

SELECT [t].[ID], 
(SELECT [t1].[KEYS], [t1].[ValueV] FROM @Test t1 WHERE t1.[ID] = [t].id FOR JSON PATH ) a
FROM @Test AS [t]
GROUP BY [t].[ID]

2
投票

我的两分钱:

有趣的是,您需要有效的单独 JSON 行,而不是单个 JSON 字符串。无论如何,这里有一些替代答案,尽管接受的答案是最好的答案。

-- 100% hardcoded yourself. Pre SQL Server 2016 
SELECT '[{"ID":' + convert(nvarchar(4),T1.[ID]) + ',"ValueV":' + convert(nvarchar(4),T1.[ValueV]) + ',"Keys":["' + T1.[Keys] + '","' + T2.[Keys] + '"]}]' AS [keys]
FROM #Test AS T1 INNER JOIN #Test T2 ON t2.ID = t1.ID AND t2.ValueV = t1.ValueV AND t2.keys > t1.keys 

或者:

-- Use the OPENJSON to output your results as a dataset and not just a single row. I've removed the escape character back slashes to match the accepted answers output  
    SELECT 
     '[' + REPLACE((REPLACE((REPLACE([value], '\','')),':"[',':[')),']"}',']}') + ']'
    FROM OPENJSON(
    (SELECT T1.[ID],T1.[ValueV], '["' + T1.[Keys] + '","' + T2.[Keys] + '"]' AS [keys]
    FROM #Test AS T1 INNER JOIN #Test T2 ON t2.ID = t1.ID AND t2.ValueV = t1.ValueV AND t2.keys > t1.keys 
    FOR JSON PATH))

或者:

-- This is a lot cleaner for an array of values in pairs. Again using OPENJSON to output your desired result.  
select 
'[' + [Value] + ']' FROM OPENJSON(
(select T1.[ID], T1.[ValueV], JSON_MODIFY(JSON_MODIFY('[]','append lax $',t0.keys),'append lax $',t1.keys) as keys
FROM #Test AS T0 inner join #Test as t1
on  t0.ID = t1.ID AND t0.ValueV = t1.ValueV AND t0.keys < t1.keys
FOR JSON path))

请注意,JSON 本身不是一个对象,而只是一串命名值对,有效的 JavaScript,但仍然是一个子集,当您想要一个嵌套的 Javascript 对象时,它看起来完全不同(如果 JS 是您想要的数据目的地) ,我在这里假设)。 JSON 函数非常适合快速提取数据以进行传输,但是当您需要嵌套输出或针对值数组而不是对象对数据集进行分组时,它可能会变得相当棘手。就我个人而言,因为它是一个字符串,所以对于更复杂的东西我只是自己构建它。希望这是一个不同的看法。


1
投票

试试这个:

SELECT (SELECT [ID], [Keys], [ValueV]  FOR JSON PATH)
FROM #Test 
GROUP BY ID, keys, ValueV

或者这个:

SELECT (SELECT [ID], [Keys], [ValueV]  FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
FROM #Test 
GROUP BY ID, keys, ValueV

1
投票

你可能会这样做:

SELECT 
(SELECT ID, ValueV, KEYS
 FROM Test Test2
 WHERE (Test2.ValueV = Test.ValueV AND Test2.ID = Test.ID) FOR JSON PATH) JSON
FROM Test
GROUP BY ValueV, ID

或者将分组列保留在 JSON 列之外:

SELECT 
ID, ValueV,
(SELECT KEYS
 FROM Test Test2
 WHERE (Test2.ValueV = Test.ValueV AND Test2.ID = Test.ID) FOR JSON PATH) JSON
FROM Test
GROUP BY ValueV, ID

0
投票

这就是我们想要的吗?

with test as 
( select 1 ValueV , N'ChangeAdress 19 - 21' KEYS , 200 id
union all
select 1, N'ChangeAdress 20 - 22', 200
union all 
select 1, N'ChangeAdress 22 - 24', 300
union all 
select 1, N'ChangeAdress 23 - 25', 300
union all 
select 2, N'ChangeAdress 24 - 26', 400
union all 
select 2, N'ChangeAdress 25 - 27', 400
union all 
select 3, N'ChangeAdress 26 - 28', 400
union all 
select 3, N'ChangeAdress 27 - 29', 400
)
select c.ValueV,id,(select * from test
where ValueV = c.ValueV and id = c.id
for json path  
)x
from test c
group by c.ValueV,c.id**
© www.soinside.com 2019 - 2024. All rights reserved.