【问题标题】:Using TSQL OPENJSON how do I extract a value from JSON array with a dynamic key name使用 TSQL OPENJSON 如何从具有动态键名的 JSON 数组中提取值
【发布时间】:2024-05-20 05:20:01
【问题描述】:

我有一个带有内部属性数组的 json 文档。在这些属性之一上,键名动态/随机更改。除了最后一个讨厌的属性之外,我可以轻松提取所有数据点。我在过去发现或使用 OPENJSON 的所有方法都依赖于已知的键名。

在“内部”数组中,第一个属性的键名会发生变化。我想提取与该动态键关联的值,但不知道该键将是什么。希望下面的代码能比我用文字更好地描述问题。

为了便于阅读,JSON 文档的格式如下所示...

{
    "outer1": {
        "inner1": {
            "dynamicKey123": "attribute1",
            "staticKey1": "attribute2",
            "staticKey2": "attribute3",
            "staticKey3": "attribute4"
        }
    },
    "outer2": {
        "inner2": {
            "dynamicKeyABC": "attribute1",
            "staticKey1": "attribute2",
            "staticKey2": "attribute3",
            "staticKey3": "attribute4"
        }
    }
}

一些要测试的代码...

CREATE TABLE openjson_test (json_col VARCHAR(MAX));

INSERT INTO openjson_test (json_col)
VALUES ('{"outer1":{"inner1":{"dynamicKey123":"attribute1","staticKey1":"attribute2","staticKey2":"attribute3","staticKey3":"attribute4"}},"outer2":{"inner2":{"dynamicKeyABC":"attribute1","staticKey1":"attribute2","staticKey2":"attribute3","staticKey3":"attribute4"}}}');

到目前为止我开发的查询带有注释掉的麻烦部分...

SELECT
    json_col,
    so.[key] AS soKey,
    si.[key] AS siKey,
    si.[value] AS siValue,
    --ar.dynamicKey, 
    ar.staticKey1,
    ar.staticKey2,
    ar.staticKey3
FROM openjson_test
CROSS APPLY OPENJSON(json_col) so
CROSS APPLY OPENJSON(json_col, '$.' + so.[key]) si
CROSS APPLY OPENJSON(json_col, '$.' + so.[key] + '.' + si.[key])
WITH (
    --dynamicKey VARCHAR(256) '$.dynamicKey???', How do I extract this value without knowing the key
    staticKey1 VARCHAR(256) '$.staticKey1',
    staticKey2 VARCHAR(256) '$.staticKey2',
    staticKey3 VARCHAR(256) '$.staticKey3'
) ar

【问题讨论】:

    标签: tsql azure-sql-database open-json


    【解决方案1】:

    我建议使用 条件聚合

    SELECT
        so.[key] AS soKey,
        si.[key] AS siKey,
        MAX(CASE WHEN attr.[key] NOT IN('staticKey1','staticKey2','staticKey3') THEN attr.[value] END) AS DynamicAttr,
        MAX(CASE WHEN attr.[key]='staticKey1' THEN attr.[value] END) AS attrKey1,
        MAX(CASE WHEN attr.[key]='staticKey2' THEN attr.[value] END) AS attrKey2,
        MAX(CASE WHEN attr.[key]='staticKey3' THEN attr.[value] END) AS attrKey3
    FROM openjson_test
    CROSS APPLY OPENJSON(json_col) so
    CROSS APPLY OPENJSON(json_col, '$.' + so.[key]) si
    CROSS APPLY OPENJSON(json_col, '$.' + so.[key] + '.' + si.[key]) attr
    GROUP BY so.[key],si.[key];
    

    此技术用于PIVOT 场景,但允许更通用的逻辑。

    【讨论】:

    • 谢谢@Shnugo。非常干净的解决方案
    【解决方案2】:

    您可以使用不带WITH 子句的OPENJSON 并过滤掉具有已知名称的列:

    SELECT json_col,
        so.[key] AS soKey,
        si.[key] AS siKey,
        si.[value] AS siValue,
        ar2.Value AS dynamicKey,
        ar.staticKey1,
        ar.staticKey2,
        ar.staticKey3
    FROM dbo.openjson_test t
    CROSS APPLY OPENJSON(t.json_col) so
    CROSS APPLY OPENJSON(json_col, '$.' + so.[key]) si
    CROSS APPLY OPENJSON(json_col, '$.' + so.[key] + '.' + si.[key])
    WITH (
        staticKey1 VARCHAR(256) '$.staticKey1',
        staticKey2 VARCHAR(256) '$.staticKey2',
        staticKey3 VARCHAR(256) '$.staticKey3'
    ) ar
    CROSS APPLY (
        SELECT *
        FROM OPENJSON(json_col, '$.' + so.[key] + '.' + si.[key])
        WHERE [Key] NOT IN ('staticKey1','staticKey2','staticKey3')
    ) ar2
    

    【讨论】: