【问题标题】:Best way to format SQL output to a JSON?将 SQL 输出格式化为 JSON 的最佳方法?
【发布时间】:2021-06-23 14:57:30
【问题描述】:

一直在尝试进行 SQL 查询并输出完美的 JSON,但我遇到了问题。该查询仅涉及表,当我使用“For JSON Auto”时,它会生成一个平面对象数组:enter image description here

但是有层次关系:

Q2CNum--- LineItems---- 标识符

我尝试使用“For JSON Path”,但它只构建每个 json 对象(例如,它不会聚合其父 LineItem 下的所有标识符)。

通过将表连接到自身,我设法让它看起来像这样(相同的查询只是额外的连接,“For JSON Path Auto”似乎像这样),它看起来更像这样:enter image description here

哪个好。问题是查询花费了将近 20 倍的时间(2 秒对 20 秒)。

也许我的自我联接写得不好,或者联接以获得这些结果效率低下。

无论如何,如果有人知道如何做到这一点,请告诉我(不能分享太多,我相信你注意到图像上的颜色遮挡)。

更新:

这是查询enter image description here 的图片。问题是我得到了一个扁平的 JSON(只是一个对象数组)。

Select OWS.Q2CNum, 
       OWS.LineItem, 
       OWS.Identifier, 
       OWS.ProductType, 
       OWS.Workstation, 
       OWS.Calculation, 
       OWS.UnitOfMeasure, 
       OWS.CurrentStatus, 
       LS.Location, 
       LS.ProgramStamp_LocalTime
FROM table1 AS OWS
INNER JOIN table2 AS LS
ON OWS.Q2CNum = LS.[Order]
AND OWS.LineItem = LS.LineItem
AND OWS.Identifier = LS.Identifier
WHERE LS.ProgramStamp_LocalTime = 
    (Select TOP 1 LS2.ProgramStamp_LocalTime
    from dbo.view_Location_Stamps_Local AS LS2 
    Where LS2.[Order] = OWS.Q2CNum
    AND LS2.LineItem= OWS.LineItem
    AND LS2.Identifier = OWS.Identifier
    Order by LS2.ProgramStamp_LocalTime Desc) 
AND LS.[Order] = @testVar
AND LS.LineItem = COALESCE('001',OWS.LineItem)
AND LS.Identifier like '%'
---Group by Q2CNum
ORDER BY Q2CNum, LineItem, Identifier DESC
FOR JSON Auto

但我需要它是分层的:

{
    Q2CNum: value,
    LineItems: [{
         LineItem: value,
         Identifiers: [{
               identifier: value,
               etc...
         }],
    }],

}

更新 2:

如果我运行这个查询,我会得到更多我想要的:

Select Q2C.Q2CNum, 
       Li.LineItem  , 
       OWS.Identifier, 
       OWS.ProductType, 
       OWS.Workstation, 
       OWS.Calculation, 
       OWS.UnitOfMeasure, 
       OWS.CurrentStatus, 
       LS.Location, 
       LS.ProgramStamp_LocalTime
FROM table1 AS OWS
INNER JOIN table2 AS Q2C
ON Q2C.Q2CNum = OWS.Q2CNum and Q2C.LineItem = OWS.LineItem and Q2C.Identifier = OWS.Identifier and Q2C.Workstation = OWS.Workstation
INNER JOIN table3 AS Li
ON Li.Q2CNum = OWS.Q2CNum and Li.LineItem = OWS.LineItem and Li.Identifier = OWS.Identifier and Li.Workstation = OWS.Workstation
INNER JOIN table4 AS LS
ON OWS.Q2CNum = LS.[Order]
AND OWS.LineItem = LS.LineItem
AND OWS.Identifier = LS.Identifier
WHERE LS.ProgramStamp_LocalTime = 
    (Select TOP 1 LS2.ProgramStamp_LocalTime
    from table5 AS LS2 
    Where LS2.[Order] = OWS.Q2CNum
    AND LS2.LineItem= OWS.LineItem
    AND LS2.Identifier = OWS.Identifier
    Order by LS2.ProgramStamp_LocalTime Desc) 
AND LS.[Order] = @testVar
AND LS.LineItem = COALESCE('001',OWS.LineItem)
AND LS.Identifier like '%'
---Group by Q2CNum
ORDER BY Q2CNum, LineItem, Identifier DESC
FOR JSON Auto

但是查询的时间比较长。

最终更新:

好的,Mike Petri 的解决方案有效。我不得不修补它并试图围绕它(仍然这样做——在此之前我对我的 SQL 技能更有信心)。无论如何,这是我使用的最终代码(上半部分并不重要,我已经编写了该查询,唯一重要的是使用临时表。我会使用视图但我有资源限制,所以这是更好的自动取款机)。真正的魔力始于 Query 的下半部分:

IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
BEGIN
    DROP TABLE #Temp;
END;

SELECT  OWS.Q2CNum
        ,OWS.LineItem
        ,OWS.Identifier
        ,OWS.ProductType
        ,OWS.Workstation
        ,OWS.Calculation
        ,OWS.UnitOfMeasure
        ,OWS.CurrentStatus
        ,LS.Location
        ,LS.ProgramStamp_LocalTime
INTO    #Temp
FROM    table1 AS OWS
        INNER JOIN table2 AS LS ON OWS.Q2CNum = LS.[Order]
                                   AND OWS.LineItem = LS.LineItem
                                   AND OWS.Identifier = LS.Identifier
WHERE   LS.ProgramStamp_LocalTime = (
                                        SELECT  TOP 1
                                                LS2.ProgramStamp_LocalTime
                                        FROM    talbe3 AS LS2
                                        WHERE   LS2.[Order] = OWS.Q2CNum
                                                AND LS2.LineItem = OWS.LineItem
                                                AND LS2.Identifier = OWS.Identifier
                                        ORDER BY
                                                LS2.ProgramStamp_LocalTime DESC
                                    )
        AND LS.[Order] = @testVar
        AND LS.LineItem = COALESCE('001', OWS.LineItem)
        AND LS.Identifier LIKE '%';

----REAL MAGIC HAPPENS HERE

SELECT t.Q2CNum
        ,(
             SELECT DISTINCT(t2.LineItem)
                    ,(
                         SELECT  DISTINCT(t3.Identifier)
                                ,t3.Location
                                ,t3.ProgramStamp_LocalTime
                                ,(
                                    SELECT   t4.Workstation
                                            ,t4.ProductType
                                            ,t4.Calculation
                                            ,t4.UnitOfMeasure
                                            ,t4.CurrentStatus
                                    FROM #Temp AS t4
                                    WHERE t3.Q2CNum = t4.Q2CNum and t3.LineItem = t4.LineItem and  t3.Identifier = t4.Identifier
                                    FOR JSON PATH 
                                ) AS Workstations
                        FROM #Temp AS t3
                        WHERE t3.Q2CNum = t2.Q2CNum and t2.LineItem = t3.LineItem
                        FOR JSON PATH
                     ) AS Identifiers
             FROM   #Temp AS t2
             WHERE  t.Q2CNum = t2.Q2CNum
             FOR JSON PATH
         ) AS LineItems
FROM    (
            SELECT  DISTINCT
                    Q2CNum
            FROM    #Temp
        ) AS t
FOR JSON AUTO;
DROP TABLE #Temp;

欢迎所有批评(我想知道的一件事是过滤(使用 Where 子句)同心 SQL 选择的正确方法,即表 t4 应该只过滤到表 t3(它是直接的层次结构父级?)还是我也应该添加t4 和 t2 的约束?还是 t4 和 t?

【问题讨论】:

  • 您使用的是哪种 DBMS 产品? “SQL”只是所有关系数据库使用的一种查询语言,而不是特定数据库产品的名称,并且 JSON 支持是高度特定于供应商的。请为您使用的数据库产品添加tagWhy should I tag my DBMS
  • 不确定您使用的是什么 DBMS,但为什么不直接在代码中进行合并而不是查询?
  • 不知道我正在使用哪个 DBMS(谷歌搜索如何找到它,但建议不起作用)。现在,我在 SQL 查询末尾使用“For JSON AUTO”或“FOR JSON PATH”来创建 JSON。我意识到我可能可以在应用程序中操作 JSON(接收数据),但在这里执行似乎更好(这样无论我在哪里发送它,它都会采用一致的形式)。
  • 请发布您正在使用的 SQL 查询(不是图片)。 DBMS 表示您正在使用的服务器平台。 SQL Server、MySQL 等
  • 对不起,我无法发布实际代码,可能会给公司带来麻烦。我了解 DBMS 的含义,只是不知道如何检查它,无论 SQL 是否具有创建 JSON 的功能。我现在正在尝试使用该功能,但它非常挑剔。我有一个连接两个表的相当简单的查询,但是为了在 JSON 中正确聚合数据,我必须多次连接其中一个表。当我这样做时,它会增加一秒的查询,两次它会增加 20 秒。我只是认为我们当然有能力将 SQL 从 SQL 转换为 JSON。

标签: sql json sql-server-2017


【解决方案1】:

看看这是否能让你更接近。 由于您涉及连接,因此引用内部/外部查询会变得很冒险。您可以将基本查询设为 VIEW,并替换 #Temp 引用,或使用下面的代码将其存储在临时表中,然后通过内部表引用外部表以获得您所追求的分组。

IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
BEGIN
    DROP TABLE #Temp;
END;

SELECT  OWS.Q2CNum
        ,OWS.LineItem
        ,OWS.Identifier
        ,OWS.ProductType
        ,OWS.Workstation
        ,OWS.Calculation
        ,OWS.UnitOfMeasure
        ,OWS.CurrentStatus
        ,LS.Location
        ,LS.ProgramStamp_LocalTime
INTO    #Temp
FROM    table1 AS OWS
        INNER JOIN table2 AS LS ON OWS.Q2CNum = LS.[Order]
                                   AND OWS.LineItem = LS.LineItem
                                   AND OWS.Identifier = LS.Identifier
WHERE   LS.ProgramStamp_LocalTime = (
                                        SELECT  TOP 1
                                                LS2.ProgramStamp_LocalTime
                                        FROM    dbo.view_Location_Stamps_Local AS LS2
                                        WHERE   LS2.[Order] = OWS.Q2CNum
                                                AND LS2.LineItem = OWS.LineItem
                                                AND LS2.Identifier = OWS.Identifier
                                        ORDER BY
                                                LS2.ProgramStamp_LocalTime DESC
                                    )
        AND LS.[Order] = @testVar
        AND LS.LineItem = COALESCE('001', OWS.LineItem)
        AND LS.Identifier LIKE '%';

SELECT  t.Q2CNum
        ,(
             SELECT t2.LineItem
                    ,(
                         SELECT t2.Identifier AS value
                                ,t2.ProductType
                                ,t2.Workstation
                                ,t2.Calculation
                                ,t2.UnitOfMeasure
                                ,t2.CurrentStatus
                                ,t2.Location
                                ,t2.ProgramStamp_LocalTime
                         FOR JSON PATH
                     ) AS Identifiers
             FROM   #Temp AS t2
             WHERE  t.Q2CNum = t2.Q2CNum
             FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
         ) AS LineItems
FROM    (
            SELECT  DISTINCT
                    Q2CNum
            FROM    #Temp
        ) AS t
FOR JSON AUTO;

DROP TABLE #Temp;

【讨论】:

  • 哇哦,这很接近,而且速度很快。它未能汇总 Q2CNum 下的所有内容,但感谢您,将尝试对其进行逆向工程
  • 正确!如果您觉得答案有帮助,请点击勾选标记为答案:-D
  • 嗯,我不认为这最终能解决它。它很好地构造了一个 JSON,但它是为每个 SQL 行/条目做的。我的基本问题是查询中存在层次关系(Q2CNum => LineItem => Identifier)。我想我可能不得不做一个递归查询。或者这是不可能的大声笑。无论哪种方式,我都点击了检查
  • 我明白了。我已经更新了代码。看看这是否能让你走得更远
  • 哥们,你真是天赐之物。认为我得到了它的工作,将发布最终解决方案。您使用的 Select 语句是否有一个词? (下次我可以用谷歌搜索的东西?我知道 Subselect 是什么,但您正在将它用于 Subselect 列,或者更确切地说是列的子属性。)我想我要问的是这些术语是否存在?我对 SQL 很满意,但还没有见过这样的东西(它是专门用于格式化 JSON 的吗?)。
【解决方案2】:

我在列表中使用“FOR JSON PATH” 和项目的“对于 JSON PATH,WITHOUT_ARRAY_WRAPPER”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    • 2011-01-11
    • 1970-01-01
    相关资源
    最近更新 更多