【问题标题】:How to select JSON Properties as column using T-SQL如何使用 T-SQL 选择 JSON 属性作为列
【发布时间】:2019-11-21 16:24:14
【问题描述】:

我有一个带有 JSON 列的表。我想选择 JSON 属性作为列。属性名称将是未知的。所以我必须使用动态SQL。基于这个SO suggestion,我能够得到属性。

CREATE TABLE [Templates]
(
    [ID] [INT] NOT NULL,
    [Template] [NVARCHAR](MAX)
)

INSERT INTO Templates(ID,Template)
VALUES (1, '{"FirName":"foo"}'),
       (2, '{"FirName":"joe","LastName":"dow"}'),
       (3, '{"LastName":"smith","Address":"1234 Test Drive"}'),
       (4, '{"City":"New York"}')

// SELECT Keys
SELECT DISTINCT(j.[key]) 
FROM Templates T
CROSS APPLY OPENJSON(T.Template) AS j

如何动态创建拟合语句/WITH 子句以选择属性作为列?如果属性不存在,那么它应该返回 null

SQL FIDDLE

【问题讨论】:

    标签: json sql-server tsql


    【解决方案1】:

    动态列需要动态 SQL。如果所需的列已知,您可以使用简单的数据透视甚至条件聚合。

    示例

    Declare @SQL varchar(max)= stuff((Select ','+QuoteName([key]) 
      From (SELECT DISTINCT(j.[key]) FROM Templates T
     CROSS APPLY OPENJSON(T.Template) AS j) A  
     Order By 1 
     For XML Path('')),1,1,'')
    
    Set @SQL  = '
    Select *
     From  (
            Select T.ID
                  ,j.[Key]
                  ,j.[Value]
             From  Templates T
             Cross Apply OpenJSON(T.Template) AS j
           ) src
    Pivot ( max(value) for [Key] in ('+ @SQL+') ) pvt 
    '
    Exec(@SQL)
    

    编辑 - 如果您不希望 ID 出现在最终结果中

    Declare @SQL varchar(max)= stuff((Select ','+QuoteName([key]) 
      From (SELECT DISTINCT(j.[key]) FROM Templates T
     CROSS APPLY OPENJSON(T.Template) AS j) A  
     Order By 1 
     For XML Path('')),1,1,'')
    
    Set @SQL  = '
    Select '+@SQL+'
     From  (
            Select T.ID
                  ,j.[Key]
                  ,j.[Value]
             From  Templates T
             Cross Apply OpenJSON(T.Template) AS j
           ) src
    Pivot ( max(value) for [Key] in ('+ @SQL+') ) pvt 
    '
    Exec(@SQL)
    

    【讨论】:

    • 正如我提到的,我事先不知道列名
    • 行得通。但是为什么必须在选择中包含ID 列?如果我不包括,那么它只选择单行?
    • @LP13 如果选择单行,则不需要 ID。比如 WHERE ID=2
    • @LP13 需要明确的是,如果没有 ID,多行将压缩为 1,结果将是可疑的。
    • @LP13 请参阅编辑以从最终结果中排除 ID。而不是 SELECT * ... 我们选择动态列
    【解决方案2】:

    另一种可能的方法是将OPENJSON() 与动态生成的WITH 子句一起使用。请注意,在这种情况下,您需要在path 表达式中使用lax 模式,以保证在找不到指定路径上的对象或值时OPENJSON() 不会引发错误。

    表:

    CREATE TABLE [Templates](
        [ID] [int] NOT NULL,
        [Template] [nvarchar](max)
    )
    INSERT INTO Templates(ID,Template)
    VALUES
    (1,'{"FirName":"foo"}'),
    (2,'{"FirName":"joe","LastName":"dow"}'),
    (3,'{"LastName":"smith","Address":"1234 Test Drive"}'),
    (4,'{"City":"New York"}')
    

    声明:

    DECLARE @stm nvarchar(max) = N''
    
    -- Dynamic explicit schema (WITH clause)
    SELECT @stm = CONCAT(
       @stm,
       N', [',
       [key],
       N'] nvarchar(max) ''lax $."',
       [key],
       '"'''
    )
    FROM (
       SELECT DISTINCT  j.[key] FROM Templates t
       CROSS APPLY OPENJSON(T.Template) AS j
    ) cte
    
    -- Statement   
    SELECT @stm = CONCAT(
       N'SELECT j.* ', 
       N'FROM Templates t ', 
       N'CROSS APPLY OPENJSON(t.Template) WITH (',
       STUFF(@stm, 1, 2, N''),
       N') j '
    )
    
    -- Execution
    PRINT @stm
    EXEC sp_executesql @stm
    

    输出:

    --------------------------------------------
    Address         City     FirName    LastName 
    --------------------------------------------
                             foo    
                             joe        dow
    1234 Test Drive                     smith
                    New York        
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-26
      • 1970-01-01
      • 2021-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      • 1970-01-01
      相关资源
      最近更新 更多