SQL Query 以逗号分隔获取 Json 内部的 Json 值

Posted

技术标签:

【中文标题】SQL Query 以逗号分隔获取 Json 内部的 Json 值【英文标题】:SQL Query to get the Json inside Json values by comma separated 【发布时间】:2020-05-04 16:47:09 【问题描述】:

我有下面的 Json 对象。我需要用逗号(,)分隔的任务名称。


  "Model": [
    
      "ModelName": "Test Model",    
      "Object": [
         
           "ID": 1,
           "Name": "ABC",
           "Task" : [
             
                TaskID : 1222,
                Name: "TaskA"
             ,
             
                TaskID : 154,
                Name: "TaskB"
             
           ]
         ,
         
           "ID": 11,
           "Name": "ABCD",
           "Task" : [
             
                TaskID : 222,
                Name: "TaskX"
             ,
             
               TaskID : 234,
               Name: "TaskY"
             
           ]
        ,         
     ]  
 ]

预期的输出应在下表中。我需要任务名称应该用逗号分隔。

ModelName   ObjectID   ObjectName  TaskName
Test Model     1          ABC      TaskA, TaskB
Test Model     11         ABCD     TaskX, TaskY

我尝试了以下查询。但我不知道如何对任务名称进行分组。

                  SELECT   S1.ModelName,
                           S2.ID  AS ObjectID, 
                           S2.Name AS ObjectName, 
                           S3.TaskName
                     FROM TableA 
                       CROSS APPLY OPENJSON(JsonData)
                         WITH (Model NVARCHAR(MAX) '$.Model[0]' AS JSON) S1
                       CROSS APPLY OPENJSON (S1.Model) 
                         WITH (Object NVARCHAR(MAX) '$.Object' AS JSON,
                               ID  INT '$.ID',
                               Name NVARCHAR(250) '$.Name') S2
                       CROSS APPLY OPENJSON (S2.Object) 
                         WITH (Task NVARCHAR(MAX) '$.Task' AS JSON ,
                               TaskName NVARCHAR(MAX) '$.TaskName') S3  

【问题讨论】:

【参考方案1】:

您需要使用STRING_AGG() 来聚合文本值,一种可能的方法(基于问题中的尝试)是以下语句。任务名称的聚合是针对Object JSON 数组中的每个项目:

表:

CREATE TABLE TableA (JsonData varchar(max))
INSERT INTO TableA (JsonData) VALUES ('
  "Model": [
    
      "ModelName": "Test Model",    
      "Object": [
         
           "ID": 1,
           "Name": "ABC",
           "Task" : [
             
                "TaskID" : 1222,
                "Name": "TaskA"
             ,
             
                "TaskID" : 154,
                "Name": "TaskB"
             
           ]
         ,
         
           "ID": 11,
           "Name": "ABCD",
           "Task" : [
             
                "TaskID" : 222,
                "Name": "TaskX"
             ,
             
               "TaskID" : 234,
               "Name": "TaskY"
             
           ]
                 
     ]  
 ]')

声明:

SELECT 
   j1.ModelName,
   j2.ObjectID, j2.ObjectName,
   c.TaskName
FROM TableA t
CROSS APPLY OPENJSON(t.JsonData, '$.Model[0]') WITH (
   ModelName varchar(50) '$.ModelName',
   Object nvarchar(max) '$.Object' AS JSON
) j1
CROSS APPLY OPENJSON(j1.Object, '$') WITH (
   ObjectID int '$.ID',
   ObjectName varchar(50) '$.Name',
   Task nvarchar(max) '$.Task' AS JSON
) j2
CROSS APPLY (
   SELECT STRING_AGG([Name], ',') AS TaskName
   FROM OPENJSON (j2.Task, '$') WITH (Name varchar(50) '$.Name')
) c

【讨论】:

【参考方案2】:

您需要使用STRING_AGG()函数,适用于DB版本SQL Server 2017及更高版本,连同下面的GROUP BY表达式为

SELECT S1.ModelName, S2.ID  AS ObjectID, S2.Name AS ObjectName, 
       STRING_AGG(S3.TaskName, ',') WITHIN GROUP (ORDER BY TaskName) AS TaskName
  FROM <rest of your query>
 GROUP BY S1.ModelName, S2.ID, S2.Name

作为一个完整的查询:

SELECT S1.ModelName, S3.ObjectID, S3.ObjectName,
       STRING_AGG(S4.TaskName, ',') WITHIN GROUP (ORDER BY S4.TaskName) AS TaskName
  FROM TableA 
 CROSS APPLY OPENJSON(JsonData)
  WITH (ModelName NVARCHAR(255) '$.Model[0].ModelName') S1
 CROSS APPLY OPENJSON (JsonData) 
  WITH (Object NVARCHAR(MAX)    '$.Model[0].Object' AS JSON) S2
 CROSS APPLY OPENJSON (S2.Object)   
  WITH (ObjectID       INT           '$.ID',
        ObjectName     NVARCHAR(255) '$.Name',
        Task           NVARCHAR(MAX) '$.Task' AS JSON) S3 
 CROSS APPLY OPENJSON (S3.Task) 
  WITH (TaskID   NVARCHAR(MAX) '$.TaskID',
        TaskName NVARCHAR(MAX) '$.Name') S4
 GROUP BY S1.ModelName, S3.ObjectID, S3.ObjectName

使用WITHIN GROUP (ORDER BY TaskName) 是可选的,如果您不想订购,那么您可以从函数中删除该部分,如下所示:

Demo

【讨论】:

通过上述查询,订单正在发生变化。我不想改变顺序。有什么我们可以修改的吗?我们也有可能重复模型名称、对象 ID 和对象名称。如果我们再次使用 Group by,就会出现问题。 然后只使用STRING_AGG(S3.TaskName, ',') AS TaskName 部分@AshokYaganti 甚至订单也在改变。不使用Group by还有其他方法吗? 我添加了一个 Demo 并根据它更改了答案中的查询 @AshokYaganti Barbaros Özhan 上面的查询是正确的,但是一些极端情况下失败了。下面的查询给了我正确的结果。感谢您在这方面帮助我。

以上是关于SQL Query 以逗号分隔获取 Json 内部的 Json 值的主要内容,如果未能解决你的问题,请参考以下文章

如何获取以逗号分隔的列值

sql SQL - 在以逗号,管道或分号或任何其他字符分隔的列中获取多个值或连接值的值

SQL 从 XML 中获取列的逗号分隔值

SQL IN 逗号分隔参数与内部查询

以逗号分隔的单独 json 内容

SQL IN 子句仅返回以逗号分隔的 ID 列表中具有第一个匹配项的行