20.11.25

Новое в SQL Server 2025 - Завораживающие возможности RegEx – множественные фильтры

Автор: Louis Davidson, Awesome Use of RegEx in SQL Server – Multiple Filters

Поскольку на этой неделе проходят Microsoft Ignite и PASS Summit, я решил, что стоит написать быстрый пост о RegEx. Буду удивлён, если SQL Server 2025 не выйдет на этой неделе, а с этим релизом появится функция, которую я жду больше всего — RegEx в SQL Server.

Одно из практических применений RegEx — это более мощная фильтрация. Один из проектов, над которыми я работаю (очень медленно) — это размещение некоторых SQL-утилит на GitHub. Утилиты для просмотра метаданных таблицы, поиска столбцов, размеров баз данных и так далее. Обычно я использую LIKE для фильтрации данных, что позволяет мне просто использовать поиск по равенству, или я также могу выполнять поиск по частичному значению, когда не знаю точно, что ищу.

Обычно я делаю что-то вроде этого запроса, когда ищу столбцы:

DECLARE @column_name_like sysname = 'name';

SELECT OBJECT_SCHEMA_NAME(columns.object_id) AS schema_name,
       objects.name AS object_name,
       type_desc AS object_type,
       columns.name AS column_name
FROM sys.columns
      JOIN sys.objects
         ON COLUMNS.object_id = objects.object_id
WHERE columns.name LIKE @column_name_like
ORDER BY schema_name, object_name, column_name;

Это возвращает все объекты, у которых есть столбец с именем 'name' в текущей базе данных. Если я хочу увидеть столбцы, которые начинаются, заканчиваются или содержат name, это достаточно легко. Я довольно часто делаю это с именами таблиц вроде 'invoice' и 'invoiceLineItem', что обычно нормально работает с фильтром типа 'invoice%'. К сожалению, часто есть таблица с таким префиксом, которую вы не хотите включать, например таблица типа invoiceStatus. С выражением LIKE было бы чрезвычайно сложно создать единственное LIKE-выражение, чтобы получить 'invoice' и 'invoiceLineItem', но не invoiceStatus. (На самом деле, я думаю, это невозможно, но не буду говорить «невозможно», пока не смогу это доказать!)

А что, если вы хотите посмотреть на все столбцы конкретно названные name, nm или thename? Или столбцы, которые заканчиваются на name или nm? С выражением LIKE это было бы непросто сделать в одном выражении, но используя функциональность RegEx, это довольно легко, потому что поиск может иметь несколько поисковых выражений, используя конструкцию «или».

Итак, если я хочу искать по множественным критериям, нам теперь нужно только одно выражение в параметре, чтобы обеспечить пользователю необходимы условия поиска. Например, скажем, вы хотите искать:

  • столбцы с именем точно 'name'
  • столбцы, которые начинаются с 'start'
  • столбцы, которые заканчиваются на 'low'

С регулярным выражением вы можете просто ввести составной критерий, используя выражение типа: '^name$|^start|low$'

DECLARE @column_name_regex sysname = '^name$|^start|low$';

SELECT OBJECT_SCHEMA_NAME(columns.object_id) AS schema_name,
       objects.name AS object_name,
       type_desc AS object_type,
       columns.name AS column_name
FROM sys.columns
      JOIN sys.objects
        ON COLUMNS.object_id = objects.object_id
WHERE REGEXP_LIKE(columns.name, @column_name_regex,'i')
--флаг нечувствительности к регистру на случай, если целевой сервер чувствителен к регистру
ORDER BY schema_name, object_name, column_name;

И да, в этот момент предикат поиска становится грязным, но он даёт вам силу делать это, не прибегая к множественным критериям (что само по себе грязно). Просто имейте в виду, что это всегда будет просмотр таблицы/индекса, поэтому это не должно быть вашим способом поиска по умолчанию.

Лучшее из двух миров

Фактически, вы могли бы, для таких ситуаций, где вы создаёте утилиту, которая запрашивает только небольшое количество данных, добавить параметры LIKE и Regex:

DECLARE @column_name_like sysname = '%',
        @column_name_regex sysname = '.?';
--любое значение кроме /n, которое не разрешено в именах

SELECT OBJECT_SCHEMA_NAME(columns.object_id) AS schema_name,
       objects.name AS object_name,
       type_desc AS object_type,
       columns.name AS column_name
FROM sys.columns
       JOIN sys.objects
          ON COLUMNS.object_id = objects.object_id
WHERE columns.name LIKE @column_name_like
  AND REGEXP_LIKE(columns.name, @column_name_regex,'i')
ORDER BY schema_name, object_name, column_name;

Теперь у вас есть лучшее из двух миров: если они не хотят использовать RegEx просто для простого фильтра, есть LIKE, а RegEx есть, если они хотят создавать более сложные поиски. Если вы решите позаботиться о надёжности кода, вы можете добавить предупреждение типа:

DECLARE @column_name_like sysname = 'filter'
,@column_name_regex sysname = 'filter'

IF @column_name_like  <> '%' AND
@column_name_regex  <> '.?'
PRINT 'Предупреждение: выражения REGEX и LIKE объединены через AND, что может быть не тем, чего вы ожидаете'

Вероятно, это то, что я буду делать со своими базовыми инструментами, потому что большую часть времени я просто ищу либо одно имя таблицы, либо, возможно, две таблицы с одинаковым суффиксом, как invoice и invoiceLineItem.

Заключение

Я так долго избегал RegEx, но чем больше я изучаю их, тем больше понимаю две вещи.

  1. RegEx нужен мне не так уж часто. LIKE делает большую часть того, что мне нужно.
  2. Есть много вещей, которые можно сделать с RegEx в одной строке, на что потребовалось бы 10 или более строк, используя LIKE, поэтому я очень рад, что изучаю их, и очень рад, что они теперь часть SQL Server.

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

Отправить комментарий