11.9.25

Новое в SQL Server 2025: Функция REGEXP_SUBSTR

Автор: Louis Davidson, REGEXP_ Functions in SQL Server 2025 – REGEXP_SUBSTR

Функция REGEXP_SUBSTR извлекает части строки на основе шаблона регулярного выражения. Она имеет сходство с функцией SUBSTRING, но есть и важные (и интересные) различия. Эта функция возвращает N-ое вхождение подстроки, которая соответствует регулярному выражению.

Когда я начал писать десятую статью в серии о регулярных выражениях в SQL Server, я должен признаться: я не знал заранее, что именно эта функция делает. Классическая функция SUBSTRING принимает строго позиционные параметры. Задана строка, указываешь начальную позицию и количество символов — и получаешь результат. Никакого сопоставления с шаблоном. К счастью для вас, изучение этого материала у меня заняло совсем немного времени.

В одной из предыдущих статей, где речь шла о REGEXP_MATCHES, я показывал, как можно увидеть все совпадения, которые регулярное выражение находит в строке. REGEXP_SUBSTR в своей простой форме возвращает скалярный результат — то есть одно совпадение.

Синтаксис функции


REGEXP_SUBSTR
(
  string_expression,
  pattern_expression [, start [, occurrence [, flags [, group ] ] ] ]
)

В простейшем виде вы передаёте значение и шаблон. В следующем примере функция ищет первые три подряд идущих цифры:


SELECT REGEXP_SUBSTR('abc1234567890987654321','\d{3}');

Результат:


123

Следующий параметр задаёт начальную позицию поиска. Например, если начать поиск с четвёртого символа:


SELECT REGEXP_SUBSTR('abc1234567890987654321','\d{3}',4);

Результат остаётся тем же:


123

Поскольку индексация начинается с 1, символ «a» находится в первой позиции, а «c» — в третьей. Но если использовать 5:


SELECT REGEXP_SUBSTR('abc1234567890987654321','\d{3}',5);

Результат:


234

Хотя параметр стартовой позиции сам по себе не очень интересен (хотя наверняка есть сценарии, где он незаменим), следующий параметр — occurrence — действительно важен. С его помощью можно сделать то, что делает REGEXP_MATCHES, только возвращая по одному совпадению за вызов:


SELECT REGEXP_SUBSTR('abc1234567890987654321','\d{3}',1,1);

Это возвращает первые три цифры:


123

А третье вхождение:


SELECT REGEXP_SUBSTR('abc1234567890987654321','\d{3}',1,3);

Возвращает 7-й, 8-й и 9-й символы:


789

Можно заметить, что при использовании REGEXP_MATCHES с тем же значением результат будет аналогичен — вы увидите все совпадения, которые можно извлечь с помощью REGEXP_SUBSTR:


SELECT match_id, match_value,
       REGEXP_SUBSTR('abc1234567890987654321','\d{3}',1,match_id) 
                                 AS REGEXP_SUBSTR_result
FROM   REGEXP_MATCHES('abc1234567890987654321','\d{3}');

Результат:


match_id  match_value    REGEXP_SUBSTR_result
--------- -------------- ----------------------
1         123            123
2         456            456
3         789            789
4         098            098
5         765            765
6         432            432

Группы

Пропустим параметр flags (он работает так же, как описывалось для других функций регулярок ранее), следующий параметр — это группы.

Как я показывал на примере REGEXP_REPLACE, существует концепция групп, которая позволяет извлекать подмножество совпадения. В функции REGEXP_REPLACE это используется для форматирования результата с помощью заполнителей. В REGEXP_SUBSTR, как помните, можно напрямую извлекать части совпадения.

В следующем примере используется шаблон '(\d{1})(\d{2})'. Без указания группы он возвращает те же три символа, что и прежде. Но если задать группу 1 и 2, то первый столбец вернёт один символ, а второй — оставшиеся два.


SELECT match_id, match_value,
  REGEXP_SUBSTR('abc1234567890987654321','(\d{1})(\d{2})',1,match_id) 
                                                       AS REGEXP_SUBSTR,
  REGEXP_SUBSTR('abc1234567890987654321','(\d{1})(\d{2})',1,match_id,'',1) 
                                                       AS REGEXP_SUBSTR_1,
  REGEXP_SUBSTR('abc1234567890987654321','(\d{1})(\d{2})',1,match_id,'',2) 
                                                       AS REGEXP_SUBSTR_2
FROM   REGEXP_MATCHES('abc1234567890987654321','(\d{1})(\d{2})');

Результат показывает, что группы разделяются:


match_id   match_value  REGEXP_SUBSTR  REGEXP_SUBSTR_1  REGEXP_SUBSTR_2
---------- ------------ -------------- ---------------- ----------------
1          123          123            1                23
2          456          456            4                56
3          789          789            7                89
4          098          098            0                98
5          765          765            7                65
6          432          432            4                32

Простой пример

Рассмотрим таблицу с телефонными номерами (или тем, что должно быть телефонными номерами). Нужно извлечь код региона — первые три цифры, если данные соответствуют заданному формату:


CREATE TABLE #numbers(phoneNumber varchar(13));
INSERT INTO #numbers 
VALUES ('111-111-1111'),('222-221-2222'),('333-333'),('44444444444');
 
SELECT phoneNumber,
      REGEXP_SUBSTR(phoneNumber,'^(\d{3})-(\d{3})-(\d{4})$',1,1,'',1) 
                                                           AS areaCode
FROM   #numbers;

Результат:


phoneNumber   areaCode
------------- -------------
111-111-1111  111
222-221-2222  222
333-333       NULL
44444444444   NULL

Обратите внимание, что здесь нельзя использовать выражение OR так, как можно было бы ожидать. Если добавить альтернативу для формата «4444444444», то всё равно будет возвращаться NULL:


SELECT phoneNumber,
       REGEXP_SUBSTR(phoneNumber,
          '^(\d{3})-(\d{3})-(\d{4})$|^(\d{3})(\d{3})(\d{4})$',1,1,'',1) 
                                                              AS areaCode
FROM #numbers;

Результат тот же. Но если изменить группу на 4:


SELECT phoneNumber,
    REGEXP_SUBSTR(phoneNumber,
       '^(\d{3})-(\d{3})-(\d{4})$|^(\d{3})(\d{3})(\d{4})$',1,1,'',4) 
                                                             AS areaCode
FROM  @numbers;

Теперь только строка «4444444444» вернёт код региона:


phoneNumber   areaCode
------------- -------------
111-111-1111  NULL
222-221-2222  NULL
333-333       NULL
4444444444    444


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

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