Получить массив значений вместо массива объектов при использовании FOR JSON

Я пытаюсь сгладить массив объектов, который построен FOR JSON,

Мой запрос выглядит так:

select 
(                           
    select id from MyTable
    where id in (select value from OPENJSON(@jsonArray))
    FOR JSON PATH
) existing,                 
(   
    select value id from OPENJSON(@jsonArray) 
    where value not in (select Id from MyTable)
    FOR JSON PATH                       
) missing
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER

Результирующий JSON:

{
    "existing": [
        {
            "id": "a00cd8f6-d1c6-4604-b235-59d3cacd5bcc"
        },
        {
            "id": "052455b6-6bf5-47d3-8bee-7ba98d7fbd50"
        }
    ],
    "missing": [
        {
            "id": "328add2d-e8f2-4a0e-af54-5b1733310170"
        }
    ]
}

Вместо этого я бы хотел:

{
    "existing": [
        {
            "id": "a00cd8f6-d1c6-4604-b235-59d3cacd5bcc"
        },
        {
            "id": "052455b6-6bf5-47d3-8bee-7ba98d7fbd50"
        }
    ],
    "missing": [
        "328add2d-e8f2-4a0e-af54-5b1733310170"            
    ]
}

Отсутствующий массив не должен содержать json-объекты, только значения. Какие-либо предложения?

2 ответа

Если вы используете SQL Server 2017, вы можете создать свой массив с JSON_QUERY а также STRING_AGG (с SQL Server 2016 вы не можете использовать STRING_AGG, так что вам придется немного поработать над estra, но следующая идея будет по-прежнему верна):

declare @missing table(id varchar(max))
declare @existing table(id varchar(max))

insert into @missing values ('a00cd8f6-d1c6-4604-b235-59d3cacd5bcc')
insert into @missing values ('052455b6-6bf5-47d3-8bee-7ba98d7fbd50')
insert into @existing values ('328add2d-e8f2-4a0e-af54-5b1733310170')

select  
(                           
    select id from @missing
    FOR JSON PATH
) existing,                
(   
    select JSON_QUERY(concat('[' , STRING_AGG(concat('"' , STRING_ESCAPE(id, 'json') , '"'),',') , ']')) 
    from @existing                 
) missing 
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER

Результат:

{
  "existing": [
    {
      "id": "a00cd8f6-d1c6-4604-b235-59d3cacd5bcc"
    },
    {
      "id": "052455b6-6bf5-47d3-8bee-7ba98d7fbd50"
    }
  ],
  "missing": [
    "328add2d-e8f2-4a0e-af54-5b1733310170"
  ]
}

Использовать добавление от курсора

DECLARE @missing nvarchar(max),
    @json nvarchar(max) = (select 
                            (                           
                                select id from MyTable
                                where id in (select value from OPENJSON(@jsonArray))
                                FOR JSON PATH
                            ) existing
                            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)

    DECLARE missing_cursor CURSOR FOR   
        select value id 
        from OPENJSON(@jsonArray)
        where value not in (select Id from MyTable)

        OPEN missing_cursor  

        FETCH NEXT FROM missing_cursor   
        INTO @missing

        WHILE @@FETCH_STATUS = 0  
        BEGIN  
            SET @json = JSON_MODIFY(@json,'append $.missing', @missing)

            FETCH NEXT FROM missing_cursor   
            INTO @missing  
        END   
        CLOSE missing_cursor;  
        DEALLOCATE missing_cursor; 

    select @json

Это не так просто, как должно быть...

AFAIK нет способа создать голый массив json с sql-сервером. Но вы можете обмануть это на строковом уровне:

DECLARE @exist TABLE(id VARCHAR(100));
DECLARE @miss TABLE(id VARCHAR(100));

INSERT INTO @exist VALUES ('exist1'),('exist2');
INSERT INTO @miss VALUES ('miss1'),('miss2');

- Это создаст массив объектов, который вы хотите

SELECT id FROM @exist
FOR JSON PATH

- И это создаст голый массив, используя несколько довольно уродливых уловок.

SELECT REPLACE(REPLACE(REPLACE(
(
    SELECT id from @miss
    FOR JSON PATH
),'"id":',''),'{',''),'}','')

- Теперь мы должны объединить оба. И снова нам нужен трюк. Мы используем JSON_QUERY() на литерале JSON, чтобы избежать кавычек.

SELECT
(
    SELECT id FROM @exist
    FOR JSON PATH
) AS existing
,JSON_QUERY(
   REPLACE(REPLACE(REPLACE(
    (
        SELECT id from @miss
        FOR JSON PATH
    ),'"id":',''),'{',''),'}','')
) AS missing
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER;

Это результат

{
  "existing":[{"id":"exist1"},{"id":"exist2"}]  <--array of objects
 ,"missing":["miss1","miss2"]                   <--array of naked values
}

Я понятия не имею, почему это не может быть сделано из коробки...

Другие вопросы по тегам