在T-SQL中构造嵌套的json数组

时间:2018-12-01 09:32:52

标签: json sql-server tsql

提供表格:

C1   C2    C3
----------------
1   'v1'  1.1
2   'v2'  2.2
3   'v3'  3.3

是否有任何“简便”的方式来以这种格式返回JSON:

{
  "columns": [ "C1", "C2", "C3" ],
  "rows": [
    [ 1, "v1", 1.1 ],
    [ 2, "v2", 2.2 ],
    [ 3, "v3", 3.3 ]
  ]
}

要从表中生成具有单个值的数组,有一个巧妙的技巧,如下所示:

 SELECT JSON_QUERY(REPLACE(REPLACE(
 (
    SELECT id
    FROM table a
    WHERE pk in (1,2)
    FOR JSON PATH
 ), '{"id":',''),'}',''))   'ids'

哪个生成

"ids": [1,2]

但是要在替换之上构造嵌套数组确实很繁琐,有人知道实现此目的的好方法吗?

1 个答案:

答案 0 :(得分:1)

好吧,您要求一种简便的方法,但以下操作并非易事:-)

棘手的部分是知道哪些值需要qout,哪些可以保持 naked

这需要通用的类型分析来查找哪些值是字符串。

我知道获取元数据的唯一方法(除了使用INFORMATIONSCHEMA.COLUMNS之类的元视图来构建动态sql之外)是XML和AUTO模式。

此XML实际上非常接近您的需求。开头有一个列列表,然后是行列表。但这当然不是JSON ...

尝试一下:

-这是一个包含您提供的值的模型表。

DECLARE @mockup TABLE(C1 INT,C2 VARCHAR(100),C3 DECIMAL(4,2));
INSERT INTO @mockup VALUES
 (1,'v1',1.1)
,(2,'v2',2.2)
,(3,'v3',3.3);

-现在我们以此为基础创建XML

DECLARE @xml XML =
(
    SELECT *
    FROM @mockup t 
    FOR XML RAW,XMLSCHEMA,TYPE
);

-使用SELECT @xml检查XML的内容以查看其内部外观

-现在可以开始实际查询了:

SELECT '{"columns":[' + 
STUFF(@xml.query('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
                  for $col in /xsd:schema/xsd:element//xsd:attribute
                  return
                  <x>,{concat("""",xs:string($col/@name),"""")}</x>
                  ').value('.','nvarchar(max)'),1,1,'') +

'],"rows":[' +
STUFF(
(
    SELECT
    ',[' + STUFF(b.query('  declare namespace xsd="http://www.w3.org/2001/XMLSchema";
                            for $attr in ./@*
                            return
                            <x>,{if(/xsd:schema/xsd:element//xsd:attribute[@name=local-name($attr)]//xsd:restriction/@base="sqltypes:varchar") then
                                    concat("""",$attr,"""")
                                    else 
                                    xs:string($attr)
                                }
                            </x>
                         ').value('.','nvarchar(max)'),1,1,'') + ']'
    FROM @xml.nodes('/*:row') B(b)
    FOR XML PATH(''),TYPE
    ).value('.','nvarchar(max)'),1,1,'') +
']}';

结果

{"columns":["C1","C2","C3"],"rows":[[3,"v3",3.30],[1,"v1",1.10],[2,"v2",2.20]]}

一些解释:

第一部分将使用XQuery查找所有列(XML模式中的xsd:attribute)并创建列名称数组。

第二部分将再次使用XQuery,以便遍历所有行并将其列值写入串联的字符串中。每个值都可以引用其在模式中的类型。每当此类型为sqltypes:varchar时,该值都会被加引号。所有其他值均保持不变。

这一般不会解决每种情况...

说实话,这更多是出于我自己的好奇心:-)想找出一种解决方法。

最好的答案可能是:使用其他工具。 SQL-Server不是这里的最佳选择;-)