OPENJSON 与动态 JSON 键值对
OPENJSON with dynamic JSON key value pair
我们有一个 JSON 文档,其中周数是动态键。我们想将它们加载到关系 table.
如果我们硬编码周数,我们就能够实现关系结果集,如下所示。但是,它看起来像是一种带有硬编码值的迂回方法。我们想让它动态化。
TSQL 中有没有办法将键值对动态映射为关系 table?
DECLARE @json NVARCHAR(MAX) = N'[
{
"ID": "1",
"Measure": "Current Sales",
"2019Week12": "33",
"2019Week13": "33",
"2019Week14": "34"
},
{
"ID": "2",
"Measure": "Current Sales",
"2019Week12": "",
"2019Week13": "10",
"2019Week14": "60"
}]';
SELECT ID,Measure, WeekNumber, Sales
FROM
( SELECT * FROM OPENJSON(@json)
with
( ID int '$.ID',
Measure VARCHAR(30) '$.Measure',
[2019Week12] INT '$."2019Week12"',
[2019Week13] INT '$."2019Week13"',
[2019Week14] INT '$."2019Week14"'
)
) as p
UNPIVOT
(
Sales FOR WeekNumber IN ([2019Week12],[2019Week13],[2019Week14])
) as unpvt
我们得到的结果集是:
+----+---------------+------------+-------+
| ID | Measure | WeekNumber | Sales |
+----+---------------+------------+-------+
| 1 | Current Sales | 2019Week12 | 33 |
| 1 | Current Sales | 2019Week13 | 33 |
| 1 | Current Sales | 2019Week14 | 34 |
| 2 | Current Sales | 2019Week12 | 0 |
| 2 | Current Sales | 2019Week13 | 10 |
| 2 | Current Sales | 2019Week14 | 60 |
+----+---------------+------------+-------+
您没有说明预期的输出。我得到的是:您想获得与上面相同的结果,而无需按字面意思指定名称。我希望我没听错:
SELECT JSON_VALUE(A.[value],'$.ID') AS ID
,JSON_VALUE(A.[value],'$.Measure') AS Measure
,B.[key] AS [varName]
,B.[value] AS [varValue]
,ROW_NUMBER() OVER(PARTITION BY JSON_VALUE(A.[value],'$.ID') ORDER BY B.[key]) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
WHERE b.[key] NOT IN('ID','Measure');
结果
+----+---------------+------------+----------+----------+
| ID | Measure | varName | varValue | RowIndex |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week12 | 33 | 1 |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week13 | 33 | 2 |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week14 | 34 | 3 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week12 | | 1 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week13 | 10 | 2 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week14 | 60 | 3 |
+----+---------------+------------+----------+----------+
简而言之:
- 我们使用
OPENJSON()
深入研究您的 json 字符串。这将 return 派生集 A
. 中包含的两个对象
- 现在我们再次使用
OPENJSON()
传入A.[value]
,也就是json对象本身。
- 这将 return 所有包含的项目,但我们在
WHERE
. 中抑制 ID
和 Measurement
- 我们使用
JSON_VALUE()
. 直接从 A.[value]
获取 ID
和 Measurement
这两个特殊列
更新
一个增强可能是这样的:
SELECT C.ID
,C.varName AS [varName]
,TRY_CAST(LEFT(C.varName,4) AS INT) AS MeasureYear
,TRY_CAST(RIGHT(C.varName,2) AS INT) AS MeasureWeek
,C.varContent AS [varValue]
,ROW_NUMBER() OVER(PARTITION BY C.ID ORDER BY C.varName) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
CROSS APPLY (SELECT JSON_VALUE(A.[value],'$.ID') AS ID
,JSON_VALUE(A.[value],'$.Measure') AS Measure
,B.[key] AS varName
,B.[value] AS varContent) C
WHERE C.varName NOT IN('ID','Measure');
想法:再添加一个 APPLY
允许 return 值作为 常规列 。这使得继续处理这些值变得更加容易,并使这些内容更具可读性。
我们有一个 JSON 文档,其中周数是动态键。我们想将它们加载到关系 table.
如果我们硬编码周数,我们就能够实现关系结果集,如下所示。但是,它看起来像是一种带有硬编码值的迂回方法。我们想让它动态化。
TSQL 中有没有办法将键值对动态映射为关系 table?
DECLARE @json NVARCHAR(MAX) = N'[
{
"ID": "1",
"Measure": "Current Sales",
"2019Week12": "33",
"2019Week13": "33",
"2019Week14": "34"
},
{
"ID": "2",
"Measure": "Current Sales",
"2019Week12": "",
"2019Week13": "10",
"2019Week14": "60"
}]';
SELECT ID,Measure, WeekNumber, Sales
FROM
( SELECT * FROM OPENJSON(@json)
with
( ID int '$.ID',
Measure VARCHAR(30) '$.Measure',
[2019Week12] INT '$."2019Week12"',
[2019Week13] INT '$."2019Week13"',
[2019Week14] INT '$."2019Week14"'
)
) as p
UNPIVOT
(
Sales FOR WeekNumber IN ([2019Week12],[2019Week13],[2019Week14])
) as unpvt
我们得到的结果集是:
+----+---------------+------------+-------+
| ID | Measure | WeekNumber | Sales |
+----+---------------+------------+-------+
| 1 | Current Sales | 2019Week12 | 33 |
| 1 | Current Sales | 2019Week13 | 33 |
| 1 | Current Sales | 2019Week14 | 34 |
| 2 | Current Sales | 2019Week12 | 0 |
| 2 | Current Sales | 2019Week13 | 10 |
| 2 | Current Sales | 2019Week14 | 60 |
+----+---------------+------------+-------+
您没有说明预期的输出。我得到的是:您想获得与上面相同的结果,而无需按字面意思指定名称。我希望我没听错:
SELECT JSON_VALUE(A.[value],'$.ID') AS ID
,JSON_VALUE(A.[value],'$.Measure') AS Measure
,B.[key] AS [varName]
,B.[value] AS [varValue]
,ROW_NUMBER() OVER(PARTITION BY JSON_VALUE(A.[value],'$.ID') ORDER BY B.[key]) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
WHERE b.[key] NOT IN('ID','Measure');
结果
+----+---------------+------------+----------+----------+
| ID | Measure | varName | varValue | RowIndex |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week12 | 33 | 1 |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week13 | 33 | 2 |
+----+---------------+------------+----------+----------+
| 1 | Current Sales | 2019Week14 | 34 | 3 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week12 | | 1 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week13 | 10 | 2 |
+----+---------------+------------+----------+----------+
| 2 | Current Sales | 2019Week14 | 60 | 3 |
+----+---------------+------------+----------+----------+
简而言之:
- 我们使用
OPENJSON()
深入研究您的 json 字符串。这将 return 派生集A
. 中包含的两个对象
- 现在我们再次使用
OPENJSON()
传入A.[value]
,也就是json对象本身。 - 这将 return 所有包含的项目,但我们在
WHERE
. 中抑制 - 我们使用
JSON_VALUE()
. 直接从
ID
和 Measurement
A.[value]
获取 ID
和 Measurement
这两个特殊列
更新
一个增强可能是这样的:
SELECT C.ID
,C.varName AS [varName]
,TRY_CAST(LEFT(C.varName,4) AS INT) AS MeasureYear
,TRY_CAST(RIGHT(C.varName,2) AS INT) AS MeasureWeek
,C.varContent AS [varValue]
,ROW_NUMBER() OVER(PARTITION BY C.ID ORDER BY C.varName) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
CROSS APPLY (SELECT JSON_VALUE(A.[value],'$.ID') AS ID
,JSON_VALUE(A.[value],'$.Measure') AS Measure
,B.[key] AS varName
,B.[value] AS varContent) C
WHERE C.varName NOT IN('ID','Measure');
想法:再添加一个 APPLY
允许 return 值作为 常规列 。这使得继续处理这些值变得更加容易,并使这些内容更具可读性。