Я читал пост Брента Озара о регулярных выражениях в SQL Server 2025 (T-SQL Has Regex in SQL Server 2025. Don’t Get Too Excited) и о том, почему они работают так, как работают. В комментариях кто-то упомянул несколько подсказок (hints), которые якобы должны улучшить ситуацию. О них написано немного, поэтому я решил сам проверить, как они себя ведут. Речь идёт о: ASSUME_FIXED_MAX_SELECTIVITY_FOR_REGEXP
и ASSUME_FIXED_MIN_SELECTIVITY_FOR_REGEXP
.
Они работают, корректируя ожидаемое количество строк, возвращаемых условием с оператором REGEXP
.
Тестовая таблица
Полный скрипт приведён в приложении (он совпадает с кодом из статьи «SQL Server REGEXP_LIKE features augment, not replace, LIKE»). Отличия: я добавил имя для ограничения PRIMARY KEY
и увеличил количество строк с 1000 до 100000. Также вставим пару значений для выборки:
INSERT INTO dbo.RegExTest(Value) VALUES ('This is my first known value'), ('This is my second known value');
Главное, что стоит отметить: у таблицы два индекса, и важен именно индекс по колонке Value
:
sp_help 'RegExTest';
Фрагмент вывода:
index_name index_description index_keys ---------------- --------------------------------------------------- ------------- PK_RegExTest clustered, unique, primary key located on PRIMARY RegExTestId Value nonclustered located on PRIMARY Value
Индекс Value
не уникальный, но посмотрим, как он работает с REGEXP_LIKE
и, для сравнения, с LIKE
.
Контрольные тесты
Начнём с запроса, возвращающего все строки:
SELECT Value FROM RegExTest;
Оценка: 100002 строк, фактическое количество — 100002. Выполнился Clustered Index Scan. Удивительно, ожидал использование индекса Value
, но строки короткие, возможно это повлияло на план.
Теперь запрос с LIKE
:
SELECT Value FROM RegExTest WHERE Value LIKE 'This is my first known value';
План использовал только индекс Value
. Прогноз: 1 строка, фактически: 1 строка.
REGEXP_LIKE без подсказок
SELECT Value FROM RegExTest WHERE REGEXP_LIKE(Value,'^This is my first known value$');
Выполнение заняло 6 секунд (на домашнем ПК). Прогноз — 9000.18 строк, при 100002 строках в таблице.
Хинт ASSUME_FIXED_MAX_SELECTIVITY_FOR_REGEXP
Используем ASSUME_FIXED_MAX_SELECTIVITY_FOR_REGEXP
:
SELECT Value FROM RegExTest WHERE REGEXP_LIKE(Value,'^This is my first known value$') OPTION (USE HINT ('ASSUME_FIXED_MAX_SELECTIVITY_FOR_REGEXP'));
Прогноз вырос до 50001 строк (половина таблицы). Реальной ускоренной работы пока нет, но алгоритм оценки держится стабильно.
Хинт ASSUME_FIXED_MIN_SELECTIVITY_FOR_REGEXP
SELECT Value FROM RegExTest WHERE REGEXP_LIKE(Value,'^This is my first known value$') OPTION (USE HINT ('ASSUME_FIXED_MIN_SELECTIVITY_FOR_REGEXP'));
Прогноз уменьшился до 500.01 строк. Но и в этом случае план не использовал индекс Value
как покрывающий, производительность не изменилась.
Важно: для маленькой таблицы это несущественно. Но в больших запросах разница между 500, 9000 и 50000 строк может радикально повлиять на выбор плана.
Принудительное использование индекса
SELECT Value FROM RegExTest WITH (index = Value) WHERE REGEXP_LIKE(Value,'^This is my first known value$'); SELECT Value FROM RegExTest WITH (index = Value) WHERE REGEXP_LIKE(Value,'^This is my first known value$') OPTION (USE HINT ('ASSUME_FIXED_MAX_SELECTIVITY_FOR_REGEXP')); SELECT Value FROM RegExTest WITH (index = Value) WHERE REGEXP_LIKE(Value,'^This is my first known value$') OPTION (USE HINT ('ASSUME_FIXED_MIN_SELECTIVITY_FOR_REGEXP'));
Прогнозы кардинальности одинаковы. Отличие: теперь выполняется Index Scan (Nonclustered) вместо Clustered Index Scan.
CREATE UNIQUE INDEX UniqueValue ON RegExTest (Value);
С уникальным индексом запрос сам использует его для сканирования. Но оценки кардинальности остаются прежними.
Вывод
Подсказки в SQL Server — не лучший инструмент, но иногда они необходимы, особенно в сложных запросах. При использовании REGEXP_LIKE
почти всегда нужно ожидать высокую стоимость выполнения. Часто придётся добавлять альтернативные фильтры, потому что LIKE
справляется не со всеми задачами.
Иногда фактическое количество строк может сильно отличаться от прогноза, и запрос будет работать медленно. Подсказки помогают скорректировать ожидания. Например:
SELECT Value FROM RegExTest WHERE REGEXP_LIKE(Value,'.*') OPTION (USE HINT ('ASSUME_FIXED_MIN_SELECTIVITY_FOR_REGEXP'));
Здесь прогноз совпадает с другими вариантами, хотя реально возвращаются все строки таблицы.
Приложение
Код для загрузки тестовых данных:
USE TempDb; GO SET NOCOUNT ON; -- Drop the table if it exists IF OBJECT_ID('dbo.RegExTest', 'U') IS NOT NULL DROP TABLE dbo.RegExTest; -- Create the table CREATE TABLE dbo.RegExTest ( RegExTestId INT IDENTITY(1,1) CONSTRAINT PK_RegExTest PRIMARY KEY, Value NVARCHAR(100) INDEX Value ); -- Declare variables DECLARE @i INT = 1; DECLARE @chars NVARCHAR(62) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; DECLARE @len INT; DECLARE @str NVARCHAR(100); DECLARE @pos INT; -- Loop to insert 100000 rows WHILE @i <= 100000 BEGIN SET @len = 10 + ABS(CHECKSUM(NEWID())) % 41; -- Length between 10 and 50 SET @str = ''; WHILE LEN(@str) < @len BEGIN SET @pos = 1 + ABS(CHECKSUM(NEWID())) % LEN(@chars); SET @str = @str + SUBSTRING(@chars, @pos, 1); END INSERT INTO dbo.RegExTest (Value) VALUES (@str); SET @i += 1; END;
Комментариев нет:
Отправить комментарий