SQL JSON_值/JSON_查询来自数组并转置到行

SQL JSON_值/JSON_查询来自数组并转置到行,sql,arrays,json,sql-server,Sql,Arrays,Json,Sql Server,我想从Microsoft SQL Server数据库中JSON字符串内的数组中提取信息 如果我有这样一个JSON对象: CREATE TABLE myTable([Id] int, [JsonInfo] varchar(max)); INSERT INTO myTable ([Id], [JsonInfo]) VALUES (1, '{ "$id": "1", "Id": "3276617

我想从Microsoft SQL Server数据库中JSON字符串内的数组中提取信息

如果我有这样一个JSON对象:

CREATE TABLE myTable([Id] int, [JsonInfo] varchar(max));

INSERT INTO myTable  ([Id], [JsonInfo])
VALUES (1, 
    '{
      "$id": "1",
      "Id": "32766177-18c7-4c2d-bbb5-02588a73ff72",
      "Metadata": [
        {
          "Identifier": "Identifier1",
          "Value": "aaa"
        },
        {
          "Identifier": "Identifier2",
          "Value": "bbb"
        },
        {
          "Identifier": "Identifier3",
          "Value": "ccc"
        },
      ],
    }'
);
我发现访问整个阵列的唯一方法是:

SELECT Id, value AS Metadata
FROM myTable t
CROSS APPLY OPENJSON( JSON_QUERY(t.JsonInfo, '$.Metadata'))
要访问阵列中的信息,我执行了以下操作:

SELECT 
    JSON_VALUE(Metadata, '$.Identifier') AS Identifier,
    JSON_VALUE(Metadata, '$.Value') AS Value
FROM
    (SELECT Id, value AS Metadata
     FROM myTable t
     CROSS APPLY OPENJSON( JSON_QUERY(t.JsonInfo, '$.Metadata'))
    );
-- Declare a variable to hold the dynamic SQL statement.
DECLARE @sql VARCHAR(MAX) = ''; -- must be initialized as an empty string.

-- Using the SQL hack to append continuous rows to a variable, we can build a list
-- of column names and their values from your JSON.
SELECT
    @sql = @sql + ', NULLIF ( ''' + md.[Value] + ''', '''' ) AS [' + md.Identifier + ']'
FROM @myTable AS mt
OUTER APPLY (

    SELECT 
        Identifier, [Value] 
    FROM OPENJSON ( JsonInfo, '$.Metadata' ) WITH (
        Identifier VARCHAR(50) '$.Identifier',
        [Value] VARCHAR(50) '$.Value'
    ) AS ids

) AS md
WHERE mt.Id = 1; -- <= note the record restriction.

-- Complete the dynamic SQL statement by adding SELECT and trimming
-- off the prefixed ", " from the column/value list by using STUFF.
SET @sql = 'SELECT ' + STUFF ( @sql, 1, 2, '' ) + ';';

-- Execute the dynamic statement.
EXEC ( @sql );
我的结果是:

Identifier  | Value
------------+--------
Identifier1 | aaa
Identifier2 | bbb
Identifier3 | ccc
结果我应该是:

Identifier1 | Identifier2 | Identifier3
------------+-------------+------------
aaa         | bbb         | ccc
具有不匹配记录的结果:

Identifier1 | Identifier2 | Identifier3 |Identifier4
------------+-------------+-------------+-----------
aaa         | bbb         | ccc         | NULL
aaa         | bbb         | NULL        | ddd
我知道我可以用PIVOT转换它,但是对于这个场景来说它似乎太复杂了


关于如何轻松实现这一点,有什么建议吗?

注意:您提供的JSON无效。为了我的例子,我不得不修改它。如果JSON示例是生产数据的外观,那么您还有其他问题需要解决

最简单的方法是对数据进行透视,如下所示:

DECLARE @myTable TABLE ( [Id] INT, [JsonInfo] VARCHAR(MAX) );

INSERT INTO @myTable ( [Id], [JsonInfo] )
VALUES (1, 
    '{
      "$id": "1",
      "Id": "32766177-18c7-4c2d-bbb5-02588a73ff72",
      "Metadata": [
        {
          "Identifier": "Identifier1",
          "Value": "aaa"
        },
        {
          "Identifier": "Identifier2",
          "Value": "bbb"
        },
        {
          "Identifier": "Identifier3",
          "Value": "ccc"
        },
        {
          "Identifier": "Identifier4",
          "Value": ""
        }
      ]
    }'
);

SELECT
    Id, md.*
FROM @myTable AS mt
OUTER APPLY (

    SELECT * FROM (

        SELECT Identifier, [Value] FROM OPENJSON ( JsonInfo, '$.Metadata' ) WITH (
            Identifier VARCHAR(50) '$.Identifier',
            [Value] VARCHAR(50) '$.Value'
        )

    ) AS ids
    PIVOT (
        MAX ( [Value] ) FOR Identifier IN ( [Identifier1], [Identifier2], [Identifier3], [Identifier4] )
    ) AS pvt

) AS md;
返回

+----+-------------+-------------+-------------+-------------+
| Id | Identifier1 | Identifier2 | Identifier3 | Identifier4 |
+----+-------------+-------------+-------------+-------------+
|  1 | aaa         | bbb         | ccc         |             |
+----+-------------+-------------+-------------+-------------+
+-------------+-------------+-------------+-------------+
| Identifier1 | Identifier2 | Identifier3 | Identifier4 |
+-------------+-------------+-------------+-------------+
| aaa         | bbb         | ccc         | NULL        |
+-------------+-------------+-------------+-------------+
这种方法只有在您知道每个标识符的预期值和行数的情况下才有效,我怀疑您已经提到过
PIVOT
。因此,您将被迫使用动态SQL方法

我不完全理解您的需求,因为我不确定您的示例中不匹配的数据来自JSON的何处,但是,继续上面的相同设置,您可以尝试以下操作:

SELECT 
    JSON_VALUE(Metadata, '$.Identifier') AS Identifier,
    JSON_VALUE(Metadata, '$.Value') AS Value
FROM
    (SELECT Id, value AS Metadata
     FROM myTable t
     CROSS APPLY OPENJSON( JSON_QUERY(t.JsonInfo, '$.Metadata'))
    );
-- Declare a variable to hold the dynamic SQL statement.
DECLARE @sql VARCHAR(MAX) = ''; -- must be initialized as an empty string.

-- Using the SQL hack to append continuous rows to a variable, we can build a list
-- of column names and their values from your JSON.
SELECT
    @sql = @sql + ', NULLIF ( ''' + md.[Value] + ''', '''' ) AS [' + md.Identifier + ']'
FROM @myTable AS mt
OUTER APPLY (

    SELECT 
        Identifier, [Value] 
    FROM OPENJSON ( JsonInfo, '$.Metadata' ) WITH (
        Identifier VARCHAR(50) '$.Identifier',
        [Value] VARCHAR(50) '$.Value'
    ) AS ids

) AS md
WHERE mt.Id = 1; -- <= note the record restriction.

-- Complete the dynamic SQL statement by adding SELECT and trimming
-- off the prefixed ", " from the column/value list by using STUFF.
SET @sql = 'SELECT ' + STUFF ( @sql, 1, 2, '' ) + ';';

-- Execute the dynamic statement.
EXEC ( @sql );
如果要
打印
已完成的@sql变量,您将看到:

SELECT NULLIF ( 'aaa', '' ) AS [Identifier1], NULLIF ( 'bbb', '' ) AS [Identifier2], NULLIF ( 'ccc', '' ) AS [Identifier3], NULLIF ( '', '' ) AS [Identifier4];

希望这能帮助您上路。

您的语句有语法错误(
JSON\u query
JSON\u值(元数据,$.Identifier)
),JSON无效。
$.Metadata
是否始终具有固定的项目计数?
$.Metadata
具有动态范围如果
myTable
表中的第二行具有
$.Metadata
JSON数组,包含五个项目,那么这两行的预期结果是什么?我在问题中添加了一个示例。如果JSON值不存在,它应该返回空值。感谢您的详细解释!我设法用动态SQL解决了这个问题。尽管我希望在JSON处理中有一个解决方案。我仍然需要测试它在处理更大数据时的效果。最终的结果必须在小型工业PC上运行,可以有数千个对象,总的来说可能有50个不同的元标记。但我相信这仍然比事后用C代码处理要好。不客气。我理解使用动态SQL的挫折感,因为这几乎是我最后的选择。也许有更好的方法可以做到这一点,但通常情况下,当您旋转未知数据时,没有很多其他选项。希望它能在你的小型电脑上正常工作。