Как я могу запросить с помощью SQL Server 2017 вложенные значения в JSON?

#arrays #json #sql-server #nested

#массивы #json #sql-сервер #вложенный

Вопрос:

Мне нужно извлечь информацию из столбца в формате JSON с помощью SQL Server. Проблема в том, что он вложен в разные объекты в массиве в объекте в массиве — и я как бы теряю след. В приведенном ниже примере кода я добираюсь только до извлечения массива actions — и тогда я застрял. К сожалению, я не слишком хорошо знаком с этим.

Я использую SQL Server 2017.

 {
    "actions":[
        {
            "class":"actions.entries.class",
            "entries":[
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"BirthDay",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":""2000-01-29T10:34:12.000Z""
                },
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"Gender",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":"Female"
                }
            ]
        }
    ]
}
  

Так, например, мне нужно найти значение для ключа «Пол», где значение для ключа «День рождения» не равно нулю: в данном случае «Женский». Для большей ясности я опустил другие объекты в массиве записей.

Любая помощь приветствуется!

Ответ №1:

Вы можете попытаться получить свои данные с помощью OPENJSON(). При таком подходе вы можете получить key/value пары из вашего вложенного JSON массива, даже если этот массив имеет разные имена ключей. Что вам нужно, так это ссылаться на объект JSON или массив с AS JSON предложением.

Ввод в формате JSON:

 DECLARE @json nvarchar(max)
SET @json = N'{
    "actions":[
        {
            "class":"actions.entries.class",
            "entries":[
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"BirthDay",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":""2000-01-29T10:34:12.000Z""
                },
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"Gender",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":"Female"
                }
            ]
        }
    ]
}'
  

Получить «Пол» и «День рождения»:

 SELECT
   t1.[value] AS Birthday,
   t2.[value] AS Gender
FROM OPENJSON(@json, '$.actions')
WITH (
   class nvarchar(max) '$.class',
   entries nvarchar(max) '$.entries' AS JSON
) j
CROSS APPLY OPENJSON(j.entries) 
WITH (
   [key] nvarchar(100) '$.key',
   [value] nvarchar(100) '$.value'
) t1
CROSS APPLY OPENJSON(j.entries) 
WITH (
   [key] nvarchar(100) '$.key',
   [value] nvarchar(100) '$.value'
) t2
WHERE 
   t1.[key] = 'Birthday' AND
   t2.[key] = 'Gender'
  

Вывод:

 Birthday                    Gender
"2000-01-29T10:34:12.000Z"  Female
  

Получить полные данные JSON:

 SELECT
   t1.class,
   t2.id, t2.[key], t2.[type], t2.[value],
   t3.class, t3.origin, t3.performanceDateTime, t3.registrationDateTime, t3.userUuid,
   t4.class1, t4.UUID1,
   t5.class2, t5.UUID2
FROM OPENJSON(@json, '$.actions')
WITH (
   class nvarchar(max) '$.class',
   entries nvarchar(max) '$.entries' AS JSON
) t1
CROSS APPLY OPENJSON(t1.entries) 
WITH (
   class nvarchar(1000) '$.class',
   id nvarchar(100) '$.id',
   [key] nvarchar(100) '$.key',
   [type] nvarchar(100) '$.type',
   [value] nvarchar(100) '$.value',
   performance nvarchar(max) '$.performance' AS JSON
) t2
CROSS APPLY OPENJSON (t2.performance) 
WITH (
   class nvarchar(1000) '$.class',
   origin nvarchar(100) '$.origin',
   performanceDateTime nvarchar(100) '$.performanceDateTime',
   registrationDateTime nvarchar(100) '$.registrationDateTime',
   userUuid nvarchar(100) '$.userUuid',
   originUuid nvarchar(max) '$.originUuid' AS JSON,
   performerUuid nvarchar(max) '$.performerUuid' AS JSON
) t3
CROSS APPLY OPENJSON (t3.originUuid) 
WITH (
   class1 nvarchar(1000) '$.class',
   UUID1 nvarchar(100) '$.UUID'
) t4
CROSS APPLY OPENJSON (t3.originUuid) 
WITH (
   class2 nvarchar(1000) '$.class',
   UUID2 nvarchar(100) '$.UUID'
) t5
  

Вывод:

 class                   id          key         type    value                       class                               origin  performanceDateTime     registrationDateTime    userUuid    class1          UUID1                                   class2          UUID2
actions.entries.class               BirthDay    O       "2000-01-29T10:34:12.000Z"  actions.entry.performance.class             1556012050827                                               java.util.UUID  3d6c5024-754f-477b-87bc-81d8e5ccadcd    java.util.UUID  3d6c5024-754f-477b-87bc-81d8e5ccadcd
actions.entries.class               Gender      O       Female                      actions.entry.performance.class             1556012050827                                               java.util.UUID  3d6c5024-754f-477b-87bc-81d8e5ccadcd    java.util.UUID  3d6c5024-754f-477b-87bc-81d8e5ccadcd
  

Обновить:

Если у вас есть данные JSON в виде значений в столбце таблицы, используйте этот подход для получения данных:

 CREATE TABLE #Data (
   JsonData nvarchar(max)
)
INSERT INTO #Data 
   (JsonData)
VALUES 
   (N'{"actions": "value1"}'),
   (N'{"actions": "value2"}')

SELECT *
FROM #Data d
CROSS APPLY OPENJSON(d.JsonData) j
  

Вывод:

 JsonData                key     value   type
{"actions": "value1"}   actions value1  1
{"actions": "value2"}   actions value2  1
  

Комментарии:

1. Спасибо, Жоров! Однако я не могу поместить JSON в подобную переменную: он должен быть прочитан из столбца («DataColumn»). Мне нужно что-то вроде DECLARE @json nvarchar(max) = JSON_QUERY(DataColumn, '$.actions') from DB.Table но это, очевидно, не работает. Что я могу здесь сделать?

2. @Cooz Use CROSS APPLY , см. Обновленный ответ. Спасибо.

3. Да, это именно то, что я искал. Большое вам спасибо.

Ответ №2:

Дайте мне знать, если это поможет вам.

 DECLARE @json nvarchar(max) = N'{
    "actions":[
        {
            "class":"actions.entries.class",
            "entries":[
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"BirthDay",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":""2000-01-29T10:34:12.000Z""
                },
                {
                    "class":"actions.entry.class",
                    "id":null,
                    "key":"Gender",
                    "performance":{
                        "class":"actions.entry.performance.class",
                        "origin":null,
                        "originUuid":{
                            "class":"java.util.UUID",
                            "UUID":"3d6c5024-754f-477b-87bc-81d8e5ccadcd"
                        },
                        "performanceDateTime":1556012050827,
                        "performerUuid":{
                            "class":"java.util.UUID",
                            "UUID":"2647a005-a3a8-4362-8f2d-ddd188f500e7"
                        },
                        "registrationDateTime":null,
                        "userUuid":null
                    },
                    "type":"O",
                    "value":"Female"
                }
            ]
        }
    ]
}';

SELECT
     [PVT].[action_index]
    ,[CNV].[BirthDay]
    ,[PVT].[Gender]
FROM
(
    SELECT
         [action_index] = [actions].[key]
        ,[entry_field] = [entries].[key]
        ,[entry_value] = [entries].[value]
    FROM OPENJSON(@json, '$.actions') AS [actions] -- iterate over actions array
    OUTER APPLY OPENJSON([actions].[value], '$.entries') -- iterate over entries array for each action
    WITH
    (
         [key] nvarchar(128)
        ,[value] nvarchar(max)
    ) AS [entries]
) AS [SRC]
PIVOT
(
    MAX([entry_value]) FOR [entry_field] IN ([BirthDay], [Gender]) -- pivot the data only for the required fields
) AS [PVT]
CROSS APPLY
(
    SELECT
         [BirthDay] = TRY_CONVERT(datetimeoffset, NULLIF(REPLACE([BirthDay], '"', ''), ''))
) AS [CNV]
WHERE (1 = 1)
    AND ([CNV].[BirthDay] IS NOT NULL);
  

Комментарии:

1. Спасибо, Гектор! Но поскольку предложение Жорова несколько более лаконично (без ‘PIVOT’, ‘TRY_CONVERT’ и так далее), я сначала попробую это. И — но это касается и ответа Жорова, поэтому я спрошу его тоже — я не могу поместить JSON в такую переменную: он должен быть прочитан из столбца («DataColumn»). Мне нужно что-то вроде DECLARE @json nvarchar(max) = JSON_QUERY(DataColumn, '$.actions') from DB.Table но это, очевидно, не работает. Что я могу здесь сделать? Еще раз спасибо!