带有两个数组的对象上的OPENJSON

时间:2019-10-25 13:40:13

标签: sql-server tsql

我在列中有以下json:

{
   "fields":[
      {
         "field":"modelName",
         "value":"abc123"
      },
      {
         "field":"displayName",
         "value":"ABC 123"
      },
      {
         "field":"order",
         "value":5
      }
   ],
   "variables":[
      {
         "varId":4,
         "oldValue":100,
         "newValue":"150"
      },
      {
         "varId":5,
         "oldValue":"abc",
         "newValue":"def"
      }
   ]
}

我想将此信息提取为以下内容:

Id  Field        Value    VarId    oldValue    newValue    
2   modelName    abc123   null     null        null
2   displayName  ABC 123  null     null        null
2   order        5        null     null        null
2   null         null     4        100         150
2   null         null     5        abc         def

这样,我就可以遍历结果集并进行空检查以查看其类型。

我目前有以下说法:

select Id, Fields.Field, Fields.Value, Variables.VarId, Variables.OldValue, Variables.NewValue from  Product
cross apply openjson( data, '$.fields') with (Field varchar(50) '$.field', Value varchar(50) '$.value') AS Fields
cross apply openjson( data, '$.variables') with (VarId int '$.varId', OldValue varchar(50) '$.oldValue', NewValue varchar(50) '$.newValue') AS Variables

但这给了我以下输出:

enter image description here

如您所见,所有内容都是重复的。可以得到我想要的输出吗?

谢谢

2 个答案:

答案 0 :(得分:2)

您需要将其作为2个单独的解析进行。这使用FULL OUTER JOIN和(公认的)哑ON子句。您还可以使用UNION ALLNULL中没有列的值:

CREATE TABLE dbo.Product (ID int,
                          [data] nvarchar(MAX));

DECLARE @JSON nvarchar(MAX) = N'{
   "fields":[
      {
         "field":"modelName",
         "value":"abc123"
      },
      {
         "field":"displayName",
         "value":"ABC 123"
      },
      {
         "field":"order",
         "value":5
      }
   ],
   "variables":[
      {
         "varId":4,
         "oldValue":100,
         "newValue":"150"
      },
      {
         "varId":5,
         "oldValue":"abc",
         "newValue":"def"
      }
   ]
}';

INSERT INTO dbo.Product (ID,
                         [data])
VALUES(2,@JSON);
GO

WITH Fields AS(
    SELECT P.Id,
           F.Field,
           F.Value,
    FROM Product P
         CROSS APPLY OPENJSON(data, '$.fields')
                     WITH (Field varchar(50) '$.field',
                           [Value] varchar(50) '$.value') F),
Variables AS(
    SELECT P.Id,
           V.VarId,
           V.OldValue,
           V.NewValue
    FROM Product P
    CROSS APPLY OPENJSON(data, '$.variables')
                WITH (VarId int '$.varId',
                      OldValue varchar(50) '$.oldValue',
                      NewValue varchar(50) '$.newValue') V)
SELECT ISNULL(F.ID,V.ID) AS ID,
       F.Field,
       F.[Value],
       V.VarId,
       V.OldValue,
       V.NewValue
FROM Fields F
     FULL OUTER JOIN Variables V ON 1 = 2; --Dumb ON clause is Dumb

答案 1 :(得分:1)

这只是另一种可能的方法(感谢@Larnu提供测试数据)。当然,您需要分别解析fieldsvariables部分,但是可以将OPENJSON()与一个显式模式(WITH子句)一起使用:

表格:

CREATE TABLE Product (
   ID int,
   [data] nvarchar(MAX)
);
DECLARE @json nvarchar(MAX) = N'{
   "fields":[
      {
         "field":"modelName",
         "value":"abc123"
      },
      {
         "field":"displayName",
         "value":"ABC 123"
      },
      {
         "field":"order",
         "value":5
      }
   ],
   "variables":[
      {
         "varId":4,
         "oldValue":100,
         "newValue":"150"
      },
      {
         "varId":5,
         "oldValue":"abc",
         "newValue":"def"
      }
   ]
}';

INSERT INTO Product (ID, [data])
VALUES
   (1, @json),
   (2, @json),
   (3, @json)

声明:

SELECT p.ID, j.*
FROM Product p
CROSS APPLY (
   SELECT *
   FROM OPENJSON (p.data, '$.fields') WITH (
      field varchar(100) '$.field',
      value varchar(100) '$.value',
      varId int '$.varId',
      oldValue varchar(100) '$.oldValue',
      newValue varchar(100) '$.newValue'
   )
   UNION ALL 
   SELECT *
   FROM OPENJSON (p.data, '$.variables') WITH (
      field varchar(100) '$.field',
      value varchar(100) '$.value',
      varId int '$.varId',
      oldValue varchar(100) '$.oldValue',
      newValue varchar(100) '$.newValue'
   )
) j
-- Additional WHERE clause
--WHERE p.ID = 2