【问题标题】:How do I return NULL conditionally from a JSON_QUERY如何从 JSON_QUERY 有条件地返回 NULL
【发布时间】:2022-03-12 18:33:35
【问题描述】:

如果从子查询(或左连接)中选择的值为空,我如何返回返回 NULL(或根本没有属性)的 JSON_QUERY?

这是一个简单的例子(想象下面的 NULL 是一个子查询):

SELECT 
    JSON_QUERY((SELECT NULL as SomeColumn 
                FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) AS Test

这会返回空 JSON 对象——这不是我想要的:

Test
----------------
{}

由于子查询返回 NULL,如果子查询返回 NULL,我希望它返回 NULL,否则它应该返回 JSON。

当子查询返回 NULL 时,JSON 结果应该是:

Test
----------------
NULL

当子查询返回 JSON 时,JSON 结果应该是:

Test
----------------
{ someColumn: 'some json' ... }

如何使用 JSON 查询执行此操作?

[编辑]

我试过这个,以为我可以做一个 CASE 语句:

SELECT 
    JSON_QUERY((SELECT JSON_VALUE('{"SomeColumn":"Test"}', '$') AS SomeColumn 
                FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) AS Test

但这不会选择根。以上返回:

Test
------
{}

用例

我追求的用例是这样的。

当我返回一个 JSON 查询时,我希望能够在地址存在时返回类似的内容:

   { 
      name: 'John',
      address: {
         line1: '123 elm st'
      }
   }

或者如果没有地址记录,我希望它返回:

   { 
      name: 'John',
      address: NULL
   }

   { 
      name: 'John'
   }

我不希望它返回

   { 
      name: 'John',
      address: {}
   }

【问题讨论】:

    标签: sql json sql-server


    【解决方案1】:

    像这样?

    SELECT NULLIF(JSON_QUERY((SELECT NULL as SomeColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)), '{}') as Test
    

    【讨论】:

    • 其实这样不行。这 `SELECT NULLIF(JSON_QUERY((SELECT top 1 value from string_split('1,2,3',',') FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)), '{}') as Test ` 返回一个字符串,而不是 JSON目的。我需要 JSON 对象。
    【解决方案2】:

    是的,您可以使用CASE..WHEN 表达式:

    WITH t AS
    (
      SELECT JSON_QUERY((SELECT JSON_VALUE('{"SomeColumn":"Test"}', '$') AS SomeColumn 
         FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) AS Test
    )     
    SELECT CASE WHEN Test = '{}' THEN 'NULL' ELSE Test END AS Test
      FROM t;
    
    
    Test
    -----
    NULL
    

    如果你想要NULL 值(布尔表达式),而不是字符串'NULL',那么使用:

    CASE WHEN Test != '{}' THEN Test END AS Test

    Demo

    【讨论】:

    • 在上面的示例中,您的 TVF 应该返回 {"SomeColumn":"Test"}。由于 Test 不为空,因此 CASE 语句应返回 {"SomeColumn":"Test"},而不是 NULL。您的示例返回 NULL 结果。当我尝试使用 CASE 语句时,我得到了同样的结果
    • 似乎使用 JSON_VALUE 并选择“$”并没有像我预期的那样选择根。
    • 嗨@pixelbits。首先,你到底想把NULL'NULL' 作为字符串得到什么?
    • 我想获取 NULL 或 JSON 对象
    • @pixelbits 如果你想得到NULL(一个布尔表达式),那么你可以使用WITH t AS ( SELECT JSON_QUERY((SELECT JSON_VALUE('{"SomeColumn":"Test"}', '$.SomeColumn') AS SomeColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) AS Test ) SELECT CASE WHEN Test != '{}' THEN Test END AS Test FROM t。考虑this
    【解决方案3】:

    如果 JSON_QUERY 未找到任何值,则返回 NULL。当您测试 JSON_QUERY=N'{}' (当没有找到任何东西时)时,它的计算结果为 FALSE。

    运行一下看看

    declare
      @json         nvarchar(max)=N'{ "objects": [ { "shape": "circle" }, {"shape": "square" } ] }',
      @nojson       nvarchar(max)=N'{}';
    
    select json_query(@json, '$.objects[0]'); /* returns { "shape": "circle" } */
    select json_query(@json, '$.objects[1]'); /* returns {"shape": "square" } */
    select json_query(@json, '$.objects[3]'); /* returns NULL */
    
    select case when json_query(@json, '$.objects[20]')=N'{}' then null else json_query(@nojson) end; 
    /* returns {} */
    
    select case when json_query(@json, '$.objects[20]') is null then 'True NULL' else 'False NULL' end;
    /* returns 'True NULL' */
    

    【讨论】:

      【解决方案4】:

      如果我对问题的理解正确,您可以尝试以下方法,以JSON_VALUE() 的结果为条件:

      SELECT JSON_VALUE(
         (SELECT NULL as SomeColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
         '$.SomeColumn'
      ) AS Test
      

      结果:

      Test
      ----
      null
      

      但如果 JSON 输出包含来自多个列的信息,您可以计算从 OPENJSON() 返回的行数作为检查条件。 OPENJSON() 的默认架构的结果是一个表,其中包含来自 JSON 文本的每个 key/value 对的单行:

      SELECT CASE WHEN EXISTS(SELECT 1 FROM OPENJSON(Json)) THEN Json END AS Test
      FROM (VALUES
         ((SELECT NULL AS SomeColumn, NULL AS OtherColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)),
         ((SELECT NULL AS SomeColumn, 123 AS OtherColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER))
      ) j (Json)
      

      结果:

      Test
      -------------------
      null
      {"OtherColumn":123}
      

      【讨论】:

      • 如果不为空,我需要选择整个 JSON 结果。您的示例仅选择一列(SomeColumn)。是否可以返回整个 JSON 结果?即如果它不为null,否则返回NULL?
      • @pixelbits 最后一个示例返回整个 JSON。谢谢。
      • 返回一个字符串对象,而不是 JSON 结果。当我尝试测试返回 JSON 结果的子查询时,这将是一个问题,因为它们将被视为字符串。
      • @pixelbits JSON_QUERY() 返回 nvarchar(max) 作为结果。没有特殊的 JSON 数据类型。所有 JSON 内容都表示为文本。但是您的用例很有趣。我仍然认为,使用OPENJSON() 作为“空”JSON(数组或对象)的检查条件是一个不错的选择。但是,当这个“空”JSON 是更复杂 JSON 中的嵌套元素时,您需要再次解析整个 JSON。老实说,基于字符串的方法使用REPLACE(Json, '{}', 'null') is also an option
      【解决方案5】:

      我试过这个,以为我可以做一个 CASE 语句:

      选择 JSON_QUERY((SELECT JSON_VALUE('{"SomeColumn":"Test"}', '$') AS SomeColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) 作为测试

      但这不会选择根目录。

      json_value() 检索 标量 值(null/boolean/string/number)。

      --"a" value is a scalar/string
      select json_value('{"a":"1234"}', '$.a') --> returns 1234
      
      --"a" value is an object
      select json_value('{"a":{"anothercolumn": "the value of anothercolumn"}}', '$.a') --> returns null, since a is not a scalar value
      

      ...因此,json_value('any json goes here', '$') 将始终返回 NULL,因为 $elf 是对象,而 json_value() 只能检索标量。

      select json_value('{"a":"b"}', '$'), json_value('[1, 2]', '$'), json_value('[{"a":"b"}, {"c":"d"}]', '$') --all NULL
      

      为了检索对象或数组的值,使用 json_query():

      select json_query('{"a":{"anothercolumn": "the value of anothercolumn"}}', '$.a') --> returns {"anothercolumn":...}, since value of "a" is an object
      --...and....
      select json_query('{"a":"b"}', '$'), json_query('[1, 2]', '$'), json_query('[{"a":"b"}, {"c":"d"}]', '$') --each json
      

      如果你想从一个子查询中检索一个“对象”,它可以返回 NULL 或一个 json 对象的字符串,请将 code from @iceblade 包装在 json_query() 中

      SELECT JSON_QUERY(
      NULLIF(JSON_QUERY((SELECT NULL as SomeColumn FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)), '{}')
      ) as Test;
      
      SELECT 
           JSON_QUERY (NULLIF((SELECT v.col as SomeColumn 
                      FOR JSON PATH, WITHOUT_ARRAY_WRAPPER), '{}'))  as 'jsonquery'
      from (values ('a'), (null), ('b')) as v(col);
      
      SELECT 
           JSON_QUERY (NULLIF((SELECT v.col as SomeColumn 
                      FOR JSON PATH, WITHOUT_ARRAY_WRAPPER), '{}'))  as 'jsonquery'
      from (values ('a'), (null), ('b')) as v(col)
      for json path, INCLUDE_NULL_VALUES;
      

      【讨论】:

        【解决方案6】:

        我找到了一个简单的解决方案。

        要在左连接(或子查询)中的记录不存在时有条件地返回 NULL 属性,请使用“点表示法”属性:

        SELECT
            EMP.firstName as [Employee.FirstName],
            EMP.lastName as [Employee.LastName],
            A.Line1 as [Employee.Address.Line1],
            A.City as [Employee.Address.City]
        FROM
           Employee EMP
        LEFT OUTER JOIN Address A ON A.Id = EMP.AddressId
        FOR JSON PATH
        

        当员工有地址时,这将返回以下内容:

        {
           employee: {
               firstName: 'john',
               lastName: 'smith',
               address: {
                  line1: 'elm st',
                  city: 'New York'
               }
           } 
        }
        

        但如果员工没有地址记录,查询将返回:

        {
           employee: {
               firstName: 'john',
               lastName: 'smith'
           } 
        }
        

        注意:

        • 您需要确保对所有点表示法属性进行分组,以便将属性和子属性放在一起

        【讨论】:

          【解决方案7】:
          SELECT ISNULL(@JSONPATHQUERY),'{}')
          

          【讨论】:

          • 您的答案可以通过额外的支持信息得到改进。请编辑您的答案以添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
          猜你喜欢
          • 1970-01-01
          • 2021-06-22
          • 2018-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-12
          相关资源
          最近更新 更多