如何使用FOR JSON PATH将具有键/值对的表查询到JSON对象中?

时间:2017-01-28 15:38:50

标签: sql-server sql-server-2016

我有一个Asset表和一个Attributes表,其中的属性是简单的键/值对。

DECLARE @Asset TABLE(AssetID INT)
INSERT @Asset VALUES (1)

DECLARE @Att TABLE (AssetID INT, Name NVARCHAR(100), Val NVARCHAR(100))
INSERT @Att VALUES (1, 'height', '100px'), (1, 'width', '200px')

我想编写一个按资产分组的查询,并包含一个包含所有属性的JSON表示的列。例如:

AssetID      Attributes
------------ -----------------------------------------------
1            {"height":"100px","width":"200px"}

如何编写查询以便属性名称值成为生成的JSON对象中的键?当我使用FOR JSON PATH时,键是列名:

SELECT
    AssetID,
    (
        SELECT Name, Val
        FROM @Att att
        WHERE att.AssetID = asset.AssetID
        FOR JSON PATH
    ) Attributes
FROM @Asset asset

返回......

AssetID      Attributes
------------ -----------------------------------------------
1            [{"Name":"height","Val":"100px"},{"Name":"width","Val":"200px"}]

3 个答案:

答案 0 :(得分:1)

我不确定在[{1}}中将列数据作为JSON获取的任何原生Key方法。 JSON名称将转换为Alias中的键值。

所以这是我的尝试

您需要透过数据来获取JSON

中所需的键值对格式

如果JSON是静态的,那么

key

WITHOUT_ARRAY_WRAPPER将删除默认

<{1}} SELECT AssetID, ( SELECT Max(CASE WHEN NAME = 'height' THEN Val END) AS height, Max(CASE WHEN NAME = 'width' THEN Val END) AS width FROM @Att att WHERE att.AssetID = asset.AssetID FOR JSON path, WITHOUT_ARRAY_WRAPPER ) Attributes FROM @Asset asset 子句JSON输出的方括号

结果:

FOR JSON

由于密钥可以是我们需要使用动态查询来转动数据的任何东西

对于演示,我已将表变量更改为临时表

+---------+--------------------------------------+
| AssetID |              Attributes              |
+---------+--------------------------------------+
|       1 | [{"height":"100px","width":"200px"}] |
+---------+--------------------------------------+

<强>结果:

CREATE TABLE #Asset
  (
     AssetID INT
  )

INSERT #Asset
VALUES (1)

CREATE TABLE #Att
  (
     AssetID INT,
     NAME    NVARCHAR(100),
     Val     NVARCHAR(100)
  )

INSERT #Att
VALUES (1,'height','100px'),
       (1,'width','200px')

DECLARE @col VARCHAR(8000)= ''

SET @col = (SELECT ',Max(CASE WHEN NAME = ''' + NAME
                   + ''' THEN Val END) as ' + Quotename(NAME)
            FROM   #Att
            FOR xml path(''))
SET @col = Stuff(@col, 1, 1, '')

EXEC ('
SELECT
    AssetID,
    (
        SELECT '+@col+'
        FROM #Att att
        WHERE att.AssetID = asset.AssetID
        FOR JSON path, WITHOUT_ARRAY_WRAPPER
    ) Attributes
FROM #Asset asset') 

答案 1 :(得分:0)

我知道您的原始问题要求使用FOR XML的解决方案,但您还说您想要一个解决方案:

  • 避免动态查询
  • 允许一组未定义的键名

您是否考虑过&#34; stuff方法&#34;完成此操作,使用STUFF函数和FOR XML

我已经成功使用了一段时间,但在某些用例中,您可能会遇到性能问题。

它不需要透视,不需要使用动态查询,并且每行允许不同数量的键/值对。

以上是使用上面示例完成的操作...

WITH 
    ASSETS AS 
    (
        SELECT 1 ASSETID, 'height' NAME, '100px' VALUE UNION
        SELECT 1 ASSETID, 'width' NAME, '200px' VALUE UNION
        SELECT 2 ASSETID, 'height' NAME, '900px' VALUE UNION
        SELECT 2 ASSETID, 'width' NAME, '1800px' VALUE UNION
        SELECT 2 ASSETID, 'randomKey' NAME, 'ABC123' VALUE
    )

SELECT 
    A.ASSETID, 
    '[' + STUFF
    (
        (
            SELECT 
                N',{"name":"' + B.NAME + '","value":"' + B.VALUE + '"}'
            FROM 
                ASSETS B
            WHERE 
                A.ASSETID = B.ASSETID               
            FOR
                XML PATH(N''), 
                TYPE
        ).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'' --the last param is blank... it replaces the leading comma with ''
    ) + ']' VALUE

FROM 
    ASSETS A
GROUP BY 
    ASSETID

输出:

ASSETID VALUE
1   [{"name":"height","value":"100px"},{"name":"width","value":"200px"}]
2   [{"name":"height","value":"900px"},{"name":"width","value":"1800px"},{"name":"randomKey","value":"ABC123"}]

我知道它并不是您最初提出的问题的完美解决方案(因为它不会使用FOR JSON作为您最初的要求,但这是一种符合您所有其他标准的解决方法

答案 2 :(得分:-1)

你也可以使用一些“假的”json作曲,如下所示:

select [AssetID],
[Attributes] = (
    select '{'+STRING_AGG ('"'+[Name]+'":"'+[Val]+'"', ',')+'}'
    from @Att
    where [AssetID] = [Asset].[AssetID]) from @Asset as [Asset]

请参阅https://docs.microsoft.com/en-us/sql/relational-databases/json/solve-common-issues-with-json-in-sql-server

上的“创建嵌套JSON结构”示例
相关问题