使用 case 语句时,SQL Server 2016 FOR JSON PATH 返回字符串而不是数组

Rol*_*rag 5 t-sql sql-server arrays json sql-server-2016

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

数组的源数据本身就是 JSON,因此我在JSON_QUERYselect 语句内部使用,并将 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
Run Code Online (Sandbox Code Playgroud)

此代码返回以下 JSON:

{  
   "data":{  
      "array1": [23439658267415,166584258534050],
      "array2": [23439658267415,166584258534050],
      "array3":"[23439658267415,166584258534050]"
   }
}
Run Code Online (Sandbox Code Playgroud)

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

我希望它返回以下 JSON:

{  
   "data":{  
      "array1": [23439658267415,166584258534050],
      "array2": [23439658267415,166584258534050],
      "array3": [23439658267415,166584258534050]
   }
}
Run Code Online (Sandbox Code Playgroud)

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

为什么会发生这种情况,如何让它在最终的 JSON 中输出数组?

Mik*_*son 4

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

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

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

for xml json对字符串值执行 a 操作会在返回的 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">
Run Code Online (Sandbox Code Playgroud)

当您将案例包装在对 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">
Run Code Online (Sandbox Code Playgroud)

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

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