【发布时间】:2017-09-30 21:03:06
【问题描述】:
sql-server OPENJSON() 函数可以将一个json数组转换成带有键值对的sql表,例如:
DECLARE @json NVARCHAR(MAX);
SET @json = '{
"key1": "val1",
"key2": "val2",
"key3": "val3"
}';
SELECT * FROM OPENJSON(@json, '$')
结果:
key value type
--------------------
key1 val1 1
key2 val2 1
key3 val3 1
将此键/值表转换回 json 数组的最佳通用方法是什么?
为什么?如果我们可以用一个函数做到这一点,它会打开一系列 json 修改,否则在 sql server 上是不可能的,例如:
- 重新排序元素
- 重命名属性(键名)
- 将 json 数组拆分为更小的数组/合并 json 数组
- 比较 json 数组(两个 json 中都存在哪些键/值元素?有什么区别?)
- 清理 json(删除语法空格/换行符以对其进行压缩)
现在,我可以开始做简单的 CONCAT('"',[key],'":"',[value]),然后做一个逗号列表聚合。但如果我想要一个既是易于在我的代码库中应用并适用于所有数据类型, 这不是一项简单的任务. 通过查看json format definition, 转换应考虑 a) 6 种不同的数据类型, b) 转义字符, c) SQL NULL/json null 处理,d) 我可能忽略的内容,即至少应该支持以下示例:
DECLARE @test_json NVARCHAR(MAX);
SET @test_json = '{
"myNull": null,
"myString": "start_\\_\"_\/_\b_\f_\n_\r_\t_\u2600_stop",
"myNumber": 3.14,
"myBool": true,
"myArray": ["1", 2],
"myObject": {"key":"val"}
}'
SELECT * FROM OPENJSON(@test_json, '$')
结果:
key value type
------------------------------------------------
myNull NULL 0
myString start_\_"_/___ _ _ _☀_stop 1
myNumber 3.14 2
myBool true 3
myArray ["1", 2] 4
myObject {"key":"val"} 5
对于字符串聚合部分,我们长期遭受“FOR XML PATH”的痛苦。幸运的是,我们在 SQL2017/AzureDB 上有 STRING_AGG(),我将接受一个取决于 STRING_AGG() 的解决方案。
【问题讨论】:
-
函数当然应该接受参数
(key nvarchar(4000), value nvarchar(max), type int),其中type是0,...,5。 -
这是一个古老但有趣的问题 (+1)。当我处于类似情况时,我使用了以下解决方案(当然,不是作为 UDF,而是基于问题中的 JSON):
SELECT CONCAT('{', STRING_AGG(t.Pair, ',') ,'}') FROM (SELECT CONCAT('"', [key], '":', CASE WHEN [type] = 0 THEN 'null' WHEN [type] = 1 THEN CONCAT('"', STRING_ESCAPE([value], 'json'), '"') WHEN [type] = 2 THEN [value] WHEN [type] = 3 THEN [value] WHEN [type] = 4 THEN JSON_QUERY([value]) WHEN [type] = 5 THEN JSON_QUERY([value]) END) AS Pair FROM OPENJSON(@test_json, '$')) t -
@Zhorov 我最终也用 CASE 编写了自定义函数。随时发布答案。
标签: sql-server json