【问题标题】:SQL to JSON - array of objects to array of values in SQL 2016SQL to JSON - SQL 2016 中的对象数组到值数组
【发布时间】:2016-06-08 17:00:08
【问题描述】:

SQL 2016 有一项新功能,可将 SQL 服务器上的数据转换为 JSON。我很难将对象数组组合成值数组,即

示例 -

CREATE TABLE #temp (item_id VARCHAR(256))

INSERT INTO #temp VALUES ('1234'),('5678'),('7890')

SELECT * FROM #temp

--convert to JSON

SELECT (SELECT item_id 
FROM #temp
FOR JSON PATH,root('ids')) 

结果 -

{
    "ids": [{
        "item_id": "1234"
    },
    {
        "item_id": "5678"
    },
    {
        "item_id": "7890"
    }]
}

但我希望结果为 -

"ids": [
        "1234",
        "5678",
        "7890"
    ]

有人可以帮帮我吗?

【问题讨论】:

标签: sql json sql-server-2016


【解决方案1】:

谢谢!我们发现的灵魂是先转换成 XML -

SELECT  
JSON_QUERY('[' + STUFF(( SELECT ',' + '"' + item_id + '"' 
FROM #temp FOR XML PATH('')),1,1,'') + ']' ) ids  
FOR JSON PATH , WITHOUT_ARRAY_WRAPPER 

【讨论】:

  • 我认为由于 FOR XML 查询的性能问题,这不是一个好习惯
  • 我们可以使用 string_escape(item_id, N'json') 来避免产生无效的 json 格式。
  • 我用这个已经有一段时间了。是否有更短的版本适用于 SQL Server 2016?
【解决方案2】:

马丁!

我相信这是一种更简单的方法:

    SELECT '"ids": ' + 
    REPLACE( 
      REPLACE( (SELECT item_id FROM #temp FOR JSON AUTO),'{"item_id":','' ),
      '"}','"' )

【讨论】:

【解决方案3】:
declare @temp table (item_id VARCHAR(256))

INSERT INTO @temp VALUES ('1234'),('5678'),('7890')

SELECT * FROM @temp

--convert to JSON

select 
    json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', char(44)))) as [json]
from @temp
for json path

当我们想将字符串连接为 json 数组时:

  1. 转义字符串 - STRING_ESCAPE

  2. 使用逗号分隔符连接字符串 - STRING_AGG,逗号 ascii 代码为 44

  3. 在括号中添加引号 - QUOTENAME(不带参数)

  4. 以 json 形式返回字符串(带有元素数组) - JSON_QUERY

【讨论】:

  • STRING_AGG 在 SQL 2016 中不可用?
  • 如果您运行的是最新版本的 SQL Server,这是最干净的解决方案。
  • 为什么 char(44) 用于逗号而不是文字 ','?
  • 虽然这是最干净的解决方案,但这里的 quotename 函数是错误的。它不仅在值周围添加括号,而且根据 SQL 转义规则,而不是 JSON,在其中转义右括号 ]。因此,如果来自@temp 的字符串值包含右括号,它将被错误地转义。正确的做法是'[' + string_agg(...) + ']'
  • 注意:对于QUOTENAME() 函数,大于 128 个字符的输入返回 NULL。
【解决方案4】:

由于原始值数组是有效的 JSON,SQL Server 的 JSON 功能中没有内置用于选择原始值数组的工具似乎很奇怪。 (如果相反有这样的功能,我至少找了好久都没有发现)。

上述方法如前所述。但是当应用于更大查询中的字段时,原语数组会被引号括起来。

例如,这个

DECLARE @BomTable TABLE (ChildNumber dbo.udt_ConMetPartNumber);
INSERT INTO @BomTable (ChildNumber) VALUES (N'101026'), (N'101027');
SELECT N'"Children": ' + REPLACE(REPLACE((SELECT ChildNumber FROM @BomTable FOR JSON PATH), N'{"ChildNumber":', N''), '"}','');

通过生产来工作:

"Children": ["101026,"101027]

但是,按照上面的方法,这个:

SELECT
    p.PartNumber,
    p.Description,
    REPLACE(REPLACE((SELECT
                        ChildNumber
                     FROM
                        Part.BillOfMaterials
                     WHERE
                        ParentNumber = p.PartNumber
                     ORDER BY
                        ChildNumber
                    FOR
                     JSON AUTO
                    ), N'{"ChildNumber":', N''), '"}', '"') AS [Children]
FROM
    Part.Parts AS p
WHERE
    p.PartNumber = N'104444'
FOR
    JSON PATH

生产:

[
    {
        "PartNumber": "104444",
        "Description": "ASSY HUB           R-SER  DRIV HP10  ABS",
        "Children": "[\"101026\",\"101027\",\"102291\",\"103430\",\"103705\",\"104103\"]"
    }
]

Children 数组被包装成一个字符串。

【讨论】:

  • 在 REPLACE 周围添加 JSON_QUERY()。这将禁用冗余转义
【解决方案5】:

这个版本(基于其他版本):

  • 正确转义特殊 JSON 字符(例如引号)
  • 返回空数组[] 表示没有数据

需要 SQL 2017 或更高版本(由于STRING_AGG):

    SELECT 
       CONCAT('[', 
            (SELECT STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', ',') 
             FROM #temp) 
        , ']')

【讨论】:

  • 更正:由于 STRING_AGG,这需要 SQL 2017 或更高版本。
【解决方案6】:

这是一个疯狂的想法,可能实用也可能不实用。使用JSON_MODIFY 对您的数据集进行递归并将内容附加到您的 JSON 数组中:

with
  d (d) as (select * from (values (1),(2),(3),(4)) t (d)),
  j (d, j) as (
    -- Adapt the recursion to make it dynamic
    select 1, json_modify('[]', 'append $', d)
    from d
    where d = 1
    union all
    select d.d, json_modify(j, 'append $', d.d)
    from d join j on d.d = j.d + 1
  )
select * 
from j;

出于说明目的,我保持简单。当然,您将对其进行调整以使其具有动态性。这会产生:

|d  |j        |
|---|---------|
|1  |[1]      |
|2  |[1,2]    |
|3  |[1,2,3]  |
|4  |[1,2,3,4]|

甚至可以用来模拟标准 SQL JSON_ARRAYAGG

【讨论】:

    【解决方案7】:

    这些解决方案中的大多数本质上都是创建一个表示数组内容的 CSV,然后将该 CSV 转换为最终的 JSON 格式。这是我用来避免使用 XML 的方法:

    DECLARE @tmp NVARCHAR(MAX) = ''
    
    SELECT @tmp = @tmp + '"' + [item_id] + '",'
    FROM #temp -- Defined and populated in the original question
    
    SELECT [ids] = JSON_QUERY((
        SELECT CASE
            WHEN @tmp IS NULL THEN '[]'
            ELSE '[' + SUBSTRING(@tmp, 0, LEN(@tmp)) + ']'
            END
        ))
    FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
    

    【讨论】:

    • SELECT $tmp = $tmp + '"' + [item_id] + '",' FROM #temp 不是连接字符串的正确方法。不能保证产生正确的结果。 (必须将 @ 替换为 $ 的评论)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多