Вопрос или проблема
У меня есть вложённый JSON без заголовков, и я не могу распарсить его с помощью SQL. Я пытаюсь загрузить эти данные в таблицу SQL.
Вот пример данных
[
[ 123, 1123, "94.05",
[
[ 11234, 31040, "100.00",
[
[ 112345, 252783, 20, "Pass", "YES"],
[ 61169237, 252785, 5, "Pass", "YES" ],
[ 61169238, 252788, 5, "Pass", "YES" ]
]
]
]
]
]
Вот мой запрос. Я умею распарсить первую часть JSON, но не вложенные. Как мне ссылаться на вложённые JSON, если у них нет заголовков?
SELECT
m.[udAuditID], m.[assessmentID],
m.category_json,
s.CatID, s.CategoryID,
s.Score, s.SecID, s.SectionID
FROM
[dbo].auditfinal m
CROSS APPLY
OPENJSON(m.category_json)
WITH (
CatID BIGINT '$[0]',
CategoryID BIGINT '$[1]',
Score VARCHAR(max) '$[2]',
SecID BIGINT '$[0][0]',
SectionID BIGINT '$[0][1]'
) s
Как вы можете видеть на приведенных ниже результатах, я могу распарсить первый набор, показанный жёлтым, но не дальше этого.
введите описание изображения здесь
Любая помощь или предложение будут очень признательны!
Если ваш JSON содержит вложенные блоки / массивы, вы можете продолжать добавлять подзапросы APPLY
до тех пор, пока не доберётесь до самого низа.
Если ваша схема JSON статична, вы, вероятно, можете составить фиксированный запрос, который даст вам все, что вам нужно. Что-то вроде этого:
-- Пример JSON-значения
declare @v nvarchar(max) = N'[
[
123,
1123,
"94.05",
[
[
11234,
31040,
"100.00",
[
[ 112345, 252783, 20, "Pass", "YES" ],
[ 61169237, 252785, 5, "Pass", "YES" ],
[ 61169238, 252788, 5, "Pass", "YES" ]
]
]
]
]
]';
select *
from openjson(@v, '$[0]') r
outer apply (
select a.[key] as [Key2], a.value as [V2], a.type as [Type2]
from openjson(r.value, '$[0]') a
where r.type = 4
) l1
outer apply (
select b.[key] as [Key3], b.value as [V3], b.type as [Type3]
from openjson(l1.V2, '$') b
where l1.Type2 = 4
) l2
outer apply (
select c.[key] as [Key4], c.value as [V4], c.type as [Type4]
from openjson(l2.V3, '$') c
where l2.Type3 = 4
) l3;
Чтобы избежать проблем, когда OPENJSON
пытается распарсить не-JSON значение, каждое из этих вложенных подзапросов имеет фильтры type = 4
, так что будут пытаться распарсить только JSON объекты, а не скалярные значения.
Если ваша структура JSON динамична и непредсказуема, вам понадобится какой-то обобщённый иерархический парсер. Пример ниже можно использовать как отправную точку. Просто не ожидайте, что это будет быстро.
-- На основе той же переменной, что и выше
with cte as (
select cast(r.[key] as varchar(max)) as [AxisPath], r.value as [Value],
r.type as [ValueType]
from openjson(@v, '$') r
union all
select c.AxisPath + '.' + cast(d.[key] as varchar(max)), d.value,
d.type
from cte c
cross apply openjson(c.Value, '$') d
where c.ValueType = 4 -- Только JSON объекты
)
select *
from cte c
-- В конце не должно остаться нераспарсенного JSON
where c.ValueType != 4
order by c.AxisPath;
Ответ или решение
Для того чтобы извлечь данные из вложенного JSON без заголовков и сохранить их в таблицу SQL, вам потребуется использовать оператор OPENJSON
многократно, чтобы глубже спуститься в иерархию JSON. Ниже описан подход, который можно использовать.
Пример кода для работы с вложенным JSON
Прежде всего, давайте объявим переменную, содержащую ваш JSON:
DECLARE @v NVARCHAR(MAX) = N'[
[
123,
1123,
"94.05",
[
[
11234,
31040,
"100.00",
[
[ 112345, 252783, 20, "Pass", "YES" ],
[ 61169237, 252785, 5, "Pass", "YES" ],
[ 61169238, 252788, 5, "Pass", "YES" ]
]
]
]
]
]';
Извлечение данных с помощью OPENJSON
Для извлечения данных из нескольких уровней вложенности используйте оператор CROSS APPLY
вместе с OPENJSON
, чтобы пройти через все уровни массива:
SELECT
init.[Value] AS CatID,
init2.[Value] AS CategoryID,
init3.[Value] AS Score,
item.[Value] AS SecID,
item2.[Value] AS SectionID,
item3.[Value] AS Value1,
item4.[Value] AS Value2,
item5.[Value] AS Value3,
item6.[Value] AS Result,
item7.[Value] AS Status
FROM
OPENJSON(@v, '$[0]') AS init
CROSS APPLY
OPENJSON(init.[Value]) AS init2
CROSS APPLY
OPENJSON(init2.[Value]) AS init3
CROSS APPLY
OPENJSON(init3.[Value]) AS items
CROSS APPLY
OPENJSON(items.[Value]) AS item
CROSS APPLY
OPENJSON(item.[Value]) AS item2
CROSS APPLY
OPENJSON(item2.[Value]) AS item3
CROSS APPLY
OPENJSON(item3.[Value]) AS item4
CROSS APPLY
OPENJSON(item4.[Value]) AS item5
CROSS APPLY
OPENJSON(item4.[Value]) AS item6
CROSS APPLY
OPENJSON(item4.[Value]) AS item7
WHERE
init.[Key] IS NOT NULL AND
init2.[Key] IS NOT NULL AND
init3.[Key] IS NOT NULL;
Объяснение кода
- OPENJSON: используется для разбора JSON-строки и извлечения значений и ключей.
- CROSS APPLY: позволяет проходить по результатам и комбинировать их, добавляя дополнительные уровни вложенности.
- Порядок вложенности: в зависимости от вашей структуры JSON, количество
CROSS APPLY
будет варьироваться. Надо внимательно следить за последовательностью вложений. - Фильтрация данных: добавляйте фильтры, чтобы исключить ненужные значения, если это необходимо.
Заключение
Это решение подходит для статичной структуры JSON, где вы знаете, какого формата будут ваши данные. Если JSON имеет динамическую структуру, возможно, потребуется более сложный подход, например, написание рекурсивного CTE, как было приведено в предыдущих примерах.
Не забудьте протестировать и адаптировать запрос под свои конкретные требования, учитывая то, как вы хотите сохранить данные в вашей SQL таблице.