Практика SQL Server

Индексируем свойства JSON через типизированные столбцы

Выведите свойство JSON в типизированный вычисляемый столбец, задайте проверки и сравните выигрыш чтения со стоимостью записи и обслуживания индекса.

JSON удобен для интеграций с меняющимися дополнительными полями. Сложность появляется, когда частый фильтр разбирает документ заново для множества строк. Узкий типизированный вычисляемый столбец позволяет обычному индексу работать с важным свойством, сохраняя исходный документ.

Отделяйте гибкие атрибуты от бизнес-ключей. Если идентификатор клиента определяет соединения, авторизацию и большинство запросов, ему нужен явный контракт. Размещение внутри JSON не отменяет тип, обязательность и допустимый диапазон.

Извлекаем нужный тип

Пример использует JSON-функции SQL Server 2016 и новее и создаёт постоянные объекты только в учебной базе.

-- Create in a disposable practice database.
CREATE TABLE dbo.JsonOrderDemo (
    DocumentId int NOT NULL PRIMARY KEY,
    Payload nvarchar(max) NOT NULL,
    CustomerId AS TRY_CONVERT(bigint, JSON_VALUE(Payload, '$.customerId')) PERSISTED,
    CONSTRAINT CK_JsonOrderDemo_Json CHECK (ISJSON(Payload) = 1),
    CONSTRAINT CK_JsonOrderDemo_Customer CHECK (CustomerId IS NOT NULL AND CustomerId > 0)
);
CREATE INDEX IX_JsonOrderDemo_Customer ON dbo.JsonOrderDemo(CustomerId);
INSERT dbo.JsonOrderDemo(DocumentId, Payload) VALUES
(1, N'{"customerId":42,"status":"new"}'),
(2, N'{"customerId":43,"status":"new"}'),
(3, N'{"customerId":42,"status":"paid"}');
SELECT DocumentId FROM dbo.JsonOrderDemo WHERE CustomerId = 42;

Начальный запрос возвращает документы 1 и 3. CustomerId вычисляется как bigint, поэтому индекс содержит числовые ключи вместо длинных текстовых результатов. На маленькой таблице оптимизатор может выбрать сканирование как более дешёвое. Пример создаёт возможный доступ, а не принуждает к конкретному плану.

TRY_CONVERT возвращает NULL для неподходящего значения. Второй CHECK явно запрещает NULL и неположительные числа. Одно условие CustomerId > 0 разрешало бы UNKNOWN при NULL и не обеспечивало обязательность поля.

ISJSON проверяет синтаксис, но не полную бизнес-схему. Корректный массив или объект без customerId ещё не являются допустимым заказом. Проверка вычисляемого столбца обнаруживает отсутствующий числовой ключ, а другим обязательным полям нужны отдельные правила.

Преобразование принимает JSON-число и строку с числом, подходящим для bigint. Если API обязан их различать, проверяйте тип токена, например через OPENJSON. Не оставляйте это различие случайному поведению преобразования.

Согласуем извлечение и поиск

Прямой фильтр по CustomerId делает доступ и тип параметров понятными. SQL Server способен распознать некоторые совпадающие вычисляемые выражения, но небольшие различия могут изменить такую возможность. Стабильный контракт надёжнее многократного ручного воспроизведения JSON-выражения.

Имена JSON-свойств чувствительны к регистру при сопоставлении пути. customerId и CustomerId не становятся одинаковыми из-за нечувствительной collation обычных сравнений. Проверяйте соглашения о названиях у всех сериализаторов интеграции.

У JSON_VALUE есть собственное поведение длины. Для традиционного nvarchar скаляр больше 4 000 символов может дать NULL в lax-режиме или ошибку в strict. Это не инструмент для неограниченного описания. OPENJSON позволяет извлекать большие скаляры с подходящей схемой.

Не индексируйте необоснованно nvarchar(4000), если свойство является числом или коротким кодом. Ограничения размера ключа и стоимость сравнения сохраняются. Но и произвольное сокращение текстового типа способно обрезать значение: сначала проверяйте допустимую длину.

Учитываем запись и изменение схемы

Изменение документа пересчитывает свойство и обслуживает индекс.

UPDATE dbo.JsonOrderDemo
SET Payload = JSON_MODIFY(Payload, '$.customerId', 43)
WHERE DocumentId = 1;
SELECT DocumentId, CustomerId FROM dbo.JsonOrderDemo ORDER BY DocumentId;

Документ 1 теперь относится к клиенту 43. Приложению не нужно отдельно обновлять зеркальный идентификатор. Один риск рассогласования исчезает, но разбор текста, ограничения, сохранённое значение и индексная запись продолжают расходовать ресурсы.

PERSISTED является выбором проекта, а не обязательным условием любого индекса вычисляемого столбца. Детерминированность, точность, типы и необходимые SET-параметры должны соответствовать правилам индексации. Сравнивайте дополнительное место и стоимость записи с сокращением чтений.

Переименование customerId или изменение его типа остаётся миграцией схемы, несмотря на гибкий формат. Согласуйте производителя, проверки, извлечение, старые строки и читателей. Развёртывание не должно незаметно превращать старые свойства в NULL или неожиданно отклонять допустимые записи.

Проверьте отсутствующие ключи, другой регистр, массивы, неверный JSON, нечисловые значения, переполнение и обычное обновление. Дополнительно определите политику повторяющихся имён свойств: синтаксическая валидность документа не является полной гарантией однозначного бизнес-содержимого. Измеряйте чтение вместе со скоростью загрузки. Полезный индекс сохраняет правильный смысл свойства при дальнейшем развитии документов.

Техническая документация: Microsoft Learn: Index JSON data · Microsoft Learn: JSON_VALUE · Microsoft Learn: ISJSON.

Вопрос по статье

Есть вопрос по этой теме?

Расскажите, что вы оцениваете или с какой проблемой столкнулись. Мы ответим с практической рекомендацией.

Inquiries are not enabled in this preview.

Задать вопрос по этой статье