【问题标题】:SQL Server 2016 FOR JSON PATH returns string instead of array when using case statement使用 case 语句时,SQL Server 2016 FOR JSON PATH 返回字符串而不是数组
【发布时间】:2016-12-18 12:50:09
【问题描述】:

我正在尝试使用 SQL Server 2016 构建一个包含数组的 JSON 对象。

数组的源数据本身就是 JSON,所以我在 select 语句中使用 JSON_QUERY,并将 FOR JSON 子句应用于 select 语句。

在我将 JSON_QUERY 子句包装在 CASE 语句中之前,一切都运行良好(在某些情况下,该数组不得包含在内,即必须为空)。

以下代码说明了问题:

declare  @projects nvarchar(max) = '{"projects": [23439658267415,166584258534050]}'
declare @id bigint = 123

SELECT 
      [data.array1] = JSON_QUERY(@projects, '$.projects') -- returns an array - perfect.
    , [data.array2] = CASE WHEN 1 is NOT NULL 
                           THEN JSON_QUERY(@projects, '$.projects') 
                           ELSE NULL END -- returns an array - still good!
    , [data.array3] = CASE WHEN @id is NOT NULL
                           THEN JSON_QUERY(@projects, '$.projects') 
                           ELSE NULL END  -- why do I end up with a string in the JSON when I do this?
FOR JSON PATH, without_array_wrapper

此代码返回以下 JSON:

{  
   "data":{  
      "array1": [23439658267415,166584258534050],
      "array2": [23439658267415,166584258534050],
      "array3":"[23439658267415,166584258534050]"
   }
}

问题是第三个“数组”作为字符串对象返回。

我希望它返回以下 JSON:

{  
   "data":{  
      "array1": [23439658267415,166584258534050],
      "array2": [23439658267415,166584258534050],
      "array3": [23439658267415,166584258534050]
   }
}

如果我删除 FOR JSON PATH... 子句,查询返回的所有列都是相同的(即 JSON_QUERY 函数返回的所有三个 nvarchar 值都是相同的)。

为什么会这样,如何让它在最终的 JSON 中输出一个数组?

【问题讨论】:

  • 你的预期输出是什么?
  • 感谢@dfundako,我已经编辑了这个问题,以便更容易理解并包含预期的输出。

标签: arrays sql-server json tsql sql-server-2016


【解决方案1】:

将 case 语句的结果包装在对 JSON_QUERY 的调用中。

, [data.array3] = JSON_QUERY(
                            CASE WHEN @id is NOT NULL
                            THEN JSON_QUERY(@projects, '$.projects') 
                            ELSE NULL END
                            )

根据documentation JSON_QUERY“从 JSON 字符串中提取对象或数组”。再往下说“返回 nvarchar(max) 类型的 JSON 片段。”。有点混乱。

对字符串值执行for xml json 会在返回的 JSON 字符串中为您提供一个字符串值,当您对 JSON 对象执行此操作时,您会在结果字符串值中内联 JSON 对象。

您可以将 CASE 视为一个函数调用,通过查看您从 CASE 返回的值来自动确定返回值。并且由于 JSON_QUERY 返回一个字符串,因此 case 将返回一个字符串,并且返回的值将是 JSON 中的字符串值。

查询计划中的case语句如下所示。

<ScalarOperator ScalarString="CASE WHEN [@id] IS NOT NULL THEN json_query([@projects],N'$.projects') ELSE NULL END">

当您将案例包装在对 JSON_QUERY 的调用中时,它看起来像这样。

<ScalarOperator ScalarString="json_query(CASE WHEN [@id] IS NOT NULL THEN json_query([@projects],N'$.projects') ELSE NULL END)">
  <Intrinsic FunctionName="json_query">

通过某种内部魔法,SQL Server 将其识别为 JSON 对象而不是字符串,并将其作为 JSON 值插入到生成的 JSON 字符串中。

CASE WHEN 1 is NOT NULL 之所以有效,是因为 SQL Server 足够聪明,可以看到 case 语句始终为真并被优化掉。

【讨论】:

  • 确实是内部魔法:select [a.b] = '[1,2]' for json path 在 JSON 中也返回一个字符串 - 所以即使文档说 JSON_QUERY 返回一个 nvarchar(max),但这并不完全如此 - 不知何故“通过内部魔法" SQL server 记住对象是一个数组,而不是一个字符串..
猜你喜欢
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 1970-01-01
  • 2018-01-22
  • 2022-01-17
  • 1970-01-01
相关资源
最近更新 更多