Я работаю с базой данных, чтобы преобразовать столбец сохраненных необработанных данных в формате JSON в формат со следующими столбцами.
UserId | QuestionText | AnswerText | LoadDate
Данные JSON следуют согласованной структуре, начиная с общего набораобщих полей, прежде чем конкретные формы ввода перечислены в массиве. Каждая запись в таблице содержит одну из этих необработанных строк JSON. Несмотря на то, что формы ввода перечислены в виде массива, для одной строки таблицы будет выбрана только одна (не более). Эти данные хранятся в базе данных SQL Server 2016, а некоторые поля в данных JSON сами являются объектами или массивами. Может быть любое количество дочерних элементов одного из этих объектов или массивов, которые сами являются объектами или массивами и т. Д.
Данные JSON возвращаются с использованием CROSS APPLY OPENJSON
или OUTER APPLY OPENJSON
вместе с WITH
ключевое слово для указания полей, которые должны быть возвращены с соответствующими типами данных. Все запросы JSON выполняются с lax
, а не с strict
.
Результирующая общая оболочка запроса выглядит примерно так:
SELECT
formData.UserId,
interests.Weekends,
interests.Holidays,
jdt.LoadDate
FROM
dbo.JsonDataTable jdt
CROSS APPLY OPENJSON(jdt.JsonField, 'lax $')
WITH
(
[Source] VARCHAR(15) '$."source"',
[UserId] VARCHAR(25) '$."UserId"',
[InterestsJson] NVARCHAR(MAX) '$."Interests"' AS JSON
) formData
OUTER APPLY OPENJSON(formData.InterestsJson, 'lax $')
WITH
(
[Weekends] VARCHAR(200) '$."weekends"',
[Holidays] VARCHAR(200) '$."holidays"'
) interests
WHERE
jdt.JsonField IS NOT NULL
AND jdt.JsonField <> ''
AND jdt.FormId = @FormId
AND jdt.FormId = formData.source
AND jdt.LoadDate BETWEEN @StartDate AND @EndDate;
Этот шаблон продолжается до тех пор, пока все дочерние массивыи объекты разрешаются в соответствующие поля, и каждое из этих объединений OPENJSON разбивает результирующий набор на количество записей, хранящихся в каждом массиве / объекте. Очевидно, что это добавляет много накладных расходов, так как число строк увеличивается с каждым дополнительным массивом, добавляемым в запрос.
Как только эти данные завершены, набор результатов помещается в операцию UNPIVOT
.
SELECT
u.UserId,
u.QuestionText,
u.AnswerText,
u.LoadDate
FROM
(
SELECT
formData.UserId,
interests.Weekends,
interests.Holidays,
jdt.LoadDate
FROM
dbo.JsonDataTable jdt
CROSS APPLY OPENJSON(jdt.JsonField, 'lax $')
WITH
(
[Source] VARCHAR(15) '$."source"',
[UserId] VARCHAR(25) '$."UserId"',
[InterestsJson] NVARCHAR(MAX) '$."Interests"' AS JSON
) formData
OUTER APPLY OPENJSON(formData.InterestsJson, 'lax $')
WITH
(
[Weekends] VARCHAR(200) '$."weekends"',
[Holidays] VARCHAR(200) '$."holidays"'
) interests
WHERE
jdt.JsonField IS NOT NULL
AND jdt.JsonField <> ''
AND jdt.FormId = @FormId
AND jdt.FormId = formData.source
AND jdt.LoadDate BETWEEN @StartDate AND @EndDate
) q
UNPIVOT
(
AnswerText
FOR QuestionText IN
(
Weekends,
Holidays
)
) u;
Это кажется очень медленным для обработки больших объемов необработанных данных JSON, хранящихся в виде поля VARCHAR (MAX) в таблице. Поля, которые фильтруются в предложении WHERE, индексируются, и индексы обрабатываются, как и ожидалось. Для одной из наиболее часто используемых форм ввода требуется около 3 минут, чтобы вернуть 15 минут данных.
ВОПРОС
Есть ли лучший подход к этому? Данные должны быть в этом формате вывода, и формат ввода не может быть изменен из необработанных строк JSON, выгруженных в столбец, который должен быть отфильтрован только для конкретной запрашиваемой формы ввода. Сами строки таблицы не могут быть отфильтрованы вне строки JSON, чтобы возвращать только данные JSON одной формы ввода. По какой-то причине эти данные были настроены таким образом, что каждая запись представляет собой совокупность всего, что было отправлено для этого пользователя, а не каждой формы ввода, отображаемой в виде отдельной строки. Я подсчитал, что для обратной загрузки данных для одной из более крупных форм ввода потребуется более 14 часов при таком подходе, и мне нужно загрузить примерно 250 форм ввода.