解压 JSON 列 SQL Server 2019 中的所有数组
Unpack all arrays in a JSON column SQL Server 2019
假设我有一个 table Schema.table
包含这些列
id | json_col
在表格上,例如
id=1
json_col ={"names":["John","Peter"],"ages":["31","40"]}
names
和 ages
的长度始终相等,但可能因 id 而异(大小至少为 1,但没有上限)。
我们如何得到一个“展开的”table - table 每个“名字”、“年龄”都有一行,例如
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
.
.
我试过 OPENJSON
和 CROSS APPLY 但下面给出了 names
和 ages
的任意组合,这是不正确的,因此我之后需要进行大量过滤
SELECT *
FROM Schema.table
CROSS APPLY OPENJSON(Schema.table,'$.names')
CROSS APPLY OPENJSON(Schema.table,'$.ages')
可能会使用条件聚合以及应用 CROSS APPLY
:
SELECT id,
MAX(CASE WHEN RowKey = 'names' THEN value END) AS names,
MAX(CASE WHEN RowKey = 'ages' THEN value END) AS ages
FROM
(
SELECT id, Q0.[value] AS RowArray, Q0.[key] AS RowKey
FROM tab
CROSS APPLY OPENJSON(JsonCol) AS Q0
) r
CROSS APPLY OPENJSON(r.RowArray) v
GROUP BY id, v.[key]
ORDER BY id, v.[key]
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
OPENJSON
的第一个参数是 JSON 列值,而不是 table 本身
这是我的建议
DECLARE @tbl TABLE(id INT,json_col NVARCHAR(MAX));
INSERT INTO @tbl VALUES(1,N'{"names":["John","Peter"],"ages":["31","40"]}')
,(2,N'{"names":["Jim"],"ages":["17"]}');
SELECT t.id
,B.[key] As ValueIndex
,B.[value] AS PersonNam
,JSON_VALUE(A.ages,CONCAT('$[',B.[key],']')) AS PersonAge
FROM @tbl t
CROSS APPLY OPENJSON(t.json_col)
WITH(names NVARCHAR(MAX) AS JSON
,ages NVARCHAR(MAX) AS JSON) A
CROSS APPLY OPENJSON(A.names) B;
简而言之:
- 我们使用带有
WITH
子句的 OPENJSON
来将 names
和 ages
读入新的 json 变量。
- 我们再用一个
OPENJSON
来“爆破”names-array
- 由于
key
是数值在数组中的位置,我们可以使用JSON_VALUE()
来读取对应的age-value的位置。
一般性评论:如果此 JSON 在您的控制之下,您应该将其更改为 entity-centered 方法(对象数组)。这样的 position dependant 存储可能是非常错误的...尝试像
{"persons":[{"name":"John","age":"31"},{"name":"Peter","age":"40"}]}
假设我有一个 table Schema.table
包含这些列
id | json_col
在表格上,例如
id=1
json_col ={"names":["John","Peter"],"ages":["31","40"]}
names
和 ages
的长度始终相等,但可能因 id 而异(大小至少为 1,但没有上限)。
我们如何得到一个“展开的”table - table 每个“名字”、“年龄”都有一行,例如
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
.
.
我试过 OPENJSON
和 CROSS APPLY 但下面给出了 names
和 ages
的任意组合,这是不正确的,因此我之后需要进行大量过滤
SELECT *
FROM Schema.table
CROSS APPLY OPENJSON(Schema.table,'$.names')
CROSS APPLY OPENJSON(Schema.table,'$.ages')
可能会使用条件聚合以及应用 CROSS APPLY
:
SELECT id,
MAX(CASE WHEN RowKey = 'names' THEN value END) AS names,
MAX(CASE WHEN RowKey = 'ages' THEN value END) AS ages
FROM
(
SELECT id, Q0.[value] AS RowArray, Q0.[key] AS RowKey
FROM tab
CROSS APPLY OPENJSON(JsonCol) AS Q0
) r
CROSS APPLY OPENJSON(r.RowArray) v
GROUP BY id, v.[key]
ORDER BY id, v.[key]
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
OPENJSON
的第一个参数是 JSON 列值,而不是 table 本身
这是我的建议
DECLARE @tbl TABLE(id INT,json_col NVARCHAR(MAX));
INSERT INTO @tbl VALUES(1,N'{"names":["John","Peter"],"ages":["31","40"]}')
,(2,N'{"names":["Jim"],"ages":["17"]}');
SELECT t.id
,B.[key] As ValueIndex
,B.[value] AS PersonNam
,JSON_VALUE(A.ages,CONCAT('$[',B.[key],']')) AS PersonAge
FROM @tbl t
CROSS APPLY OPENJSON(t.json_col)
WITH(names NVARCHAR(MAX) AS JSON
,ages NVARCHAR(MAX) AS JSON) A
CROSS APPLY OPENJSON(A.names) B;
简而言之:
- 我们使用带有
WITH
子句的OPENJSON
来将names
和ages
读入新的 json 变量。 - 我们再用一个
OPENJSON
来“爆破”names-array - 由于
key
是数值在数组中的位置,我们可以使用JSON_VALUE()
来读取对应的age-value的位置。
一般性评论:如果此 JSON 在您的控制之下,您应该将其更改为 entity-centered 方法(对象数组)。这样的 position dependant 存储可能是非常错误的...尝试像
{"persons":[{"name":"John","age":"31"},{"name":"Peter","age":"40"}]}