【问题标题】:Dynamic JSON Parsing using T-SQL使用 T-SQL 进行动态 JSON 解析
【发布时间】:2020-10-21 14:34:38
【问题描述】:

我有一个高度非结构化的 JSON 字符串,作为调用 REST API 的响应:

{
    "info": "Test Json Structure",
    "Owner": "Self-Owned",
    "managedObjects": [{
            "Name": "Device1",
            "Class": "A"
        }, {
            "Name": "Device2",
            "Class_145": "Ax01",
            "Class_11": "B",
            "Type_125478": {
                "Model": "1",
                "Manufacturer": "External"
            },
            "Type_SD": {
                "Model": "00",
                "Manufacturer": "Internal"
                }
        }, {
            "Name": "Device3",
            "Class_x": "Cx11",
            "Class_T": "C8Y",
            "Type": {
                "Model": "1x",
                "Manufacturer": "Internal"
            }
        }
    ]
}

如何使用 T-SQL 动态解析这个对象,让所有子元素代表一个表的列?更重要的是,如何处理TypeType_125478Type_SD 对象,实际上它们具有相同的结构(Model, Manufacturer),但它们的名称有些不同。另请注意,设备可能会发送一个新的标识符 (Type_XYZ),它以前不存在,但幸运的是具有相同的结构 (Model, Manufacturer)。

【问题讨论】:

  • 所以每次查询的结果都会有不同的列数,这取决于你是否得到很多"Class_145""Class_11"s?
  • 我认为最好有 type_name 和 type_value 这样的类型而不是很多列,因为你只能有结果集的固定布局
  • @GSerg 对此的一种解决方案是创建完整的列集,与 json 匹配的列将具有值,其他列将具有 NULL ..这是 R 在解析此类动态字符串时所做的。问题是如何在 T-SQL 中做同样的事情
  • @astentx 肯定会简化问题,但我无法控制源发送数据的方式
  • SQL-Server 的哪个版本?在 v2016 中引入了 JSON 支持。

标签: json sql-server tsql sql-server-2017 sql-server-json


【解决方案1】:

您可以使用类似的方法将全部分解为表格结构并继续执行此操作(需要 SQL-Server v2016+ 版本):

DECLARE @YourJSON NVARCHAR(MAX)=
N'{
    "info": "Test Json Structure",
    "Owner": "Self-Owned",
    "managedObjects": [{
            "Name": "Device1",
            "Class": "A"
        }, {
            "Name": "Device2",
            "Class_145": "Ax01",
            "Class_11": "B",
            "Type_125478": {
                "Model": "1",
                "Manufacturer": "External"
            },
            "Type_SD": {
                "Model": "00",
                "Manufacturer": "Internal"
                }
        }, {
            "Name": "Device3",
            "Class_x": "Cx11",
            "Class_T": "C8Y",
            "Type": {
                "Model": "1x",
                "Manufacturer": "Internal"
            }
        }
    ]
}';

--查询

SELECT A.info
      ,A.[Owner]
      ,C.[key] AS TagName
      ,CASE WHEN D.Model IS NULL THEN C.[value] END AS TagValue 
      ,D.Model
      ,D.Manufacturer
FROM OPENJSON(@YourJSON)
WITH(info NVARCHAR(MAX)
    ,[Owner] NVARCHAR(MAX)
    ,managedObjects NVARCHAR(MAX) AS JSON) A
OUTER APPLY OPENJSON(A.managedObjects) B
OUTER APPLY OPENJSON(B.[value]) C
OUTER APPLY OPENJSON(CASE WHEN ISJSON(C.[value])=1 THEN C.[value] END) 
WITH (Model NVARCHAR(MAX)
     ,Manufacturer NVARCHAR(MAX))D;

--结果

+---------------------+------------+-------------+----------+-------+--------------+
| info                | Owner      | TagName     | TagValue | Model | Manufacturer |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Name        | Device1  |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Class       | A        |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Name        | Device2  |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Class_145   | Ax01     |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Class_11    | B        |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Type_125478 |          | 1     | External     |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Type_SD     |          | 00    | Internal     |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Name        | Device3  |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Class_x     | Cx11     |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Class_T     | C8Y      |       |              |
+---------------------+------------+-------------+----------+-------+--------------+
| Test Json Structure | Self-Owned | Type        |          | 1x    | Internal     |
+---------------------+------------+-------------+----------+-------+--------------+

提示:您可以将B.[key] 作为对象标识符添加到结果中。

简单的想法

  • 我们使用第一个 OPENJSON 进入您的 JSON。 WITH-clause 允许将 JSON-props 寻址为列。我们将 managedObejcts 作为自己的 JSON 返回。
  • 我们再使用一个OPENJSON 来深入了解托管对象。
  • 这将返回一个对象数组。我们可以将value 传递给另一个OPENJSON
  • 只要 value 可以单独解释为 JSON,我们就再使用一个 OPENJSON,这次再次使用 WITH 子句来获取内部道具作为列。

您可以将此结果插入到表中(声明的、临时的、物理的...)并继续使用此易于阅读的集。

【讨论】:

  • 感谢@Shnugo 确实有效,因为我使用的是 SQL Server 2017..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-17
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多