SQL 服务器 JSON 路径动态列名称

SQL Server For JSON Path dynamic column name

我们正在探索 JSON feature in SQL Sever,对于其中一个场景,我们想提出一个 SQL 可以 return 一个 JSON 如下

[
  {
    "field": {
      "uuid": "uuid-field-1"
    },
    "value": {
      "uuid": "uuid-value" //value is an object
    }
  },
  {
    "field": {
      "uuid": "uuid-field-2"
    },
    "value": "1". //value is simple integer
  }
  ... more rows
]

value 字段可以是简单的 integer/string 或嵌套对象。

我们可以想出一个 table 如下所示:

field.uuid  | value.uuid | value|
------------|----------  | -----|
uuid-field-1| value-uuid | null |
uuid-field-2| null       | 1    |
  ... more rows

但是一旦我们申请 for json path,它就失败了

Property 'value' cannot be generated in JSON output due to a conflict with another column name or alias. Use different names and aliases for each column in SELECT list.

是否有可能以某种方式生成它?该值将在 value.uuidvalue 中,而不是两者?

注意: 如果我们可以将每一行转换为单独的 JSON 并将它们全部添加到一个数组中,我们愿意接受这种可能性。

这个错误的原因是(如documentation中提到的)... FOR JSON PATH 子句使用列别名或列名来确定JSON 输出中的键名。如果别名包含点,则 PATH 选项会创建嵌套对象 。在您的情况下 value.uuidvalue 都生成一个名称为 value.

的密钥

我可以推荐一种方法(可能不是最好的方法),它使用 JSON_MODIFY() 从一个空的 JSON 数组生成预期的 JSON:

Table:

CREATE TABLE Data (
   [field.uuid] varchar(100),
   [value.uuid] varchar(100), 
   [value] int
)
INSERT INTO Data 
   ([field.uuid], [value.uuid], [value])
VALUES   
   ('uuid-field-1', 'value-uuid', NULL),
   ('uuid-field-2', NULL,         1),
   ('uuid-field-3', NULL,         3),
   ('uuid-field-4', NULL,         4)

声明:

DECLARE @json nvarchar(max) = N'[]'
SELECT @json = JSON_MODIFY(
   @json, 
   'append $', 
   JSON_QUERY(
      CASE
         WHEN [value.uuid] IS NOT NULL THEN (SELECT d.[field.uuid], [value.uuid] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
         WHEN [value] IS NOT NULL THEN (SELECT d.[field.uuid], [value] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
      END   
   )   
)   
FROM Data d   
SELECT @json

结果:

[
    {
        "field":{
            "uuid":"uuid-field-1"
        },
        "value":{
            "uuid":"value-uuid"
        }
    },
    {
        "field":{
            "uuid":"uuid-field-2"
        },
        "value":1
    },
    {
        "field":{
            "uuid":"uuid-field-3"
        },
        "value":3
    },
    {
        "field":{
            "uuid":"uuid-field-4"
        },
        "value":4
    }
]
select
    json_query((select v.[field.uuid] as 'uuid' for json path, without_array_wrapper)) as 'field',
    value as 'value',
    json_query((select v.[value.uuid] as 'uuid' where v.[value.uuid] is not null for json path, without_array_wrapper)) as 'value'
from
(
values 
    ('uuid-field-1', 'value-uuid1', null),
    ('uuid-field-2', null,  2),
    ('uuid-field-3', 'value-uuid3', null),
    ('uuid-field-4', null,  4)
) as v([field.uuid], [value.uuid], value)
for json auto;--, without_array_wrapper;