sqlpostgresqlregexp-replaceregex

REGEXP_REPLACE в SQL: как заменять текст по шаблону

REGEXP_REPLACE заменяет в строке все совпадения по регулярному выражению: разбираем сигнатуру в PostgreSQL, флаги g и i, обратные ссылки на группы, расхождения POSIX и PCRE, аналоги в MySQL и ClickHouse.

12 мин чтенияСправочникsql · postgresql · regexp-replace · regex · mysql · clickhouse

Обычная функция REPLACE хороша, когда мы точно знаем, какую подстроку нужно заменить.

Например:

SELECT REPLACE('hello world', 'world', 'SQL') AS result;

Результат:

hello SQL

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

+1 (555) 123-45-67

или так:

1-555-123-4567

или так:

555 123 45 67

Если нужно оставить только цифры, обычный REPLACE уже неудобен. Придётся отдельно удалять пробелы, скобки, плюсы, дефисы и другие символы.

Вот здесь и помогает REGEXP_REPLACE.

Эта функция заменяет не конкретный кусок текста, а всё, что подходит под регулярное выражение.

Например:

SELECT REGEXP_REPLACE('+1 (555) 123-45-67', '[^0-9]', '', 'g') AS digits;

Результат:

15551234567

Мы сказали базе: «Найди все символы, которые не являются цифрами, и замени их на пустую строку». В итоге от телефона остались только цифры.

Чем REGEXP_REPLACE отличается от REPLACE

REPLACE ищет точное совпадение.

SELECT REPLACE('a-b-c', '-', '+') AS result;

Результат:

a+b+c

Здесь всё просто: каждый дефис заменили на плюс.

А REGEXP_REPLACE ищет совпадение по шаблону.

Например:

SELECT REGEXP_REPLACE('a1b2c3', '[0-9]', '', 'g') AS result;

Результат:

abc

Шаблон [0-9] означает «любая цифра от 0 до 9». Поэтому функция нашла 1, 2, 3 и удалила их.

Разница такая:

REPLACE         -> заменить конкретный текст
REGEXP_REPLACE  -> заменить всё, что подходит под шаблон

Если нужно заменить точную строку — берите REPLACE.

Если нужно заменить класс символов, разные варианты написания или сложный шаблон — берите REGEXP_REPLACE.

Базовый синтаксис в PostgreSQL

В PostgreSQL базовая форма выглядит так:

REGEXP_REPLACE(source, pattern, replacement [, flags])

Где:

  • source — исходная строка;
  • pattern — регулярное выражение, то есть шаблон поиска;
  • replacement — на что заменить найденный текст;
  • flags — дополнительные флаги, например g или i.

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

SELECT REGEXP_REPLACE('hello   world', '\s+', ' ', 'g') AS result;

Результат:

hello world

Здесь шаблон \s+ означает «один или несколько пробельных символов подряд». Мы заменили любую такую группу на один обычный пробел.

То есть строка:

hello   world

превратилась в:

hello world

Самый важный флаг: g

В PostgreSQL у REGEXP_REPLACE есть важная особенность: без флага g функция заменяет только первое совпадение.

Например:

SELECT REGEXP_REPLACE('a-b-c', '-', '+') AS result;

Результат:

a+b-c

Заменился только первый дефис.

Чтобы заменить все дефисы, нужно добавить флаг g:

SELECT REGEXP_REPLACE('a-b-c', '-', '+', 'g') AS result;

Результат:

a+b+c

g означает global, то есть «по всей строке».

Для новичка это, пожалуй, главная ловушка. Если вы чистите телефоны, пробелы, мусорные символы или повторяющиеся разделители, почти всегда нужен флаг g.

Плохой пример:

SELECT REGEXP_REPLACE('+1 (555) 123-45-67', '[^0-9]', '') AS digits;

Результат будет не таким, как хотелось бы:

1 (555) 123-45-67

Удалился только первый символ, который не является цифрой, то есть +.

Правильный вариант:

SELECT REGEXP_REPLACE('+1 (555) 123-45-67', '[^0-9]', '', 'g') AS digits;

Результат:

15551234567

Удаление лишних символов из телефона

Один из самых понятных примеров — нормализация телефона.

В таблице могут быть такие значения:

phone
--------------------
+1 (555) 123-45-67
1-555-123-4567
555 123 45 67

Если нам нужна только последовательность цифр, можно написать:

SELECT
    phone,
    REGEXP_REPLACE(phone, '[^0-9]', '', 'g') AS digits_only
FROM users;

Результат:

phone                | digits_only
---------------------+-------------
+1 (555) 123-45-67   | 15551234567
1-555-123-4567       | 15551234567
555 123 45 67        | 5551234567

Шаблон '[^0-9]' означает «любой символ, кроме цифры».

А пустая строка в замене '' означает «удалить найденное».

Получается простое правило: чтобы оставить только цифры, удаляем всё, что не цифра.

Схлопывание пробелов

Второй частый сценарий — убрать лишние пробелы.

Например, пользователь ввёл имя так:

  Ivan     Petrov

Внутри много лишних пробелов. Сначала можно схлопнуть несколько пробелов подряд в один:

SELECT REGEXP_REPLACE('Ivan     Petrov', '\s+', ' ', 'g') AS clean_name;

Результат:

Ivan Petrov

Шаблон '\s+' означает «один или несколько пробельных символов подряд».

Это не только обычный пробел. В зависимости от данных туда могут попадать табы и другие пробельные символы.

Если нужно ещё убрать пробелы по краям строки, добавьте TRIM:

SELECT TRIM(REGEXP_REPLACE('  Ivan     Petrov  ', '\s+', ' ', 'g')) AS clean_name;

Результат:

Ivan Petrov

Здесь сначала REGEXP_REPLACE превращает все группы пробелов в один пробел, а потом TRIM убирает пробелы в начале и конце строки.

Очистка имён

Допустим, в таблице есть имена, куда случайно попали лишние пробелы:

name
----------------
Ivan    Petrov
 Maria   Ivanova
Alex     Smirnov

Можно привести их к более аккуратному виду:

SELECT
    name AS old_name,
    TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g')) AS clean_name
FROM users;

Результат:

old_name         | clean_name
-----------------+--------------
Ivan    Petrov   | Ivan Petrov
 Maria   Ivanova | Maria Ivanova
Alex     Smirnov | Alex Smirnov

Это хороший пример, где REGEXP_REPLACE не меняет смысл данных, а просто убирает технический мусор.

Но перед массовым UPDATE всё равно лучше сначала посмотреть результат через SELECT.

Перед UPDATE сначала сделайте SELECT

Если вы хотите почистить данные в таблице, не начинайте сразу с обновления.

Плохая идея:

UPDATE users
SET name = TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'));

Такой запрос сразу перепишет данные. Если шаблон оказался неправильным, можно испортить много строк.

Лучше сначала посмотреть старое и новое значение рядом:

SELECT
    id,
    name AS old_name,
    TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g')) AS new_name
FROM users
WHERE name <> TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'))
ORDER BY id;

Так вы увидите, какие строки реально изменятся.

И только после проверки можно делать UPDATE:

UPDATE users
SET name = TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'))
WHERE name <> TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'));

Это простая, но очень важная привычка при чистке данных.

Флаг i: замена без учёта регистра

Флаг i включает поиск без учёта регистра.

Например, нужно привести все варианты gmail к одному виду.

В данных могут быть такие email:

alex@GMAIL.com
maria@gmail.com
ivan@Gmail.com

Запрос:

SELECT
    email,
    REGEXP_REPLACE(email, 'gmail', 'gmail', 'gi') AS normalized_email
FROM users;

Здесь флаги:

g  -> заменить все совпадения
i  -> искать без учёта регистра

Результат:

email            | normalized_email
-----------------+------------------
alex@GMAIL.com   | alex@gmail.com
maria@gmail.com  | maria@gmail.com
ivan@Gmail.com   | ivan@gmail.com

Флаги можно комбинировать в одной строке: 'gi'.

Порядок обычно не важен: gi и ig читаются одинаково.

Флаг i на простом примере

Без флага i регистр важен:

SELECT REGEXP_REPLACE('SQL sql Sql', 'sql', 'DB', 'g') AS result;

Результат:

SQL DB Sql

Заменилось только sql в нижнем регистре.

С флагом i:

SELECT REGEXP_REPLACE('SQL sql Sql', 'sql', 'DB', 'gi') AS result;

Результат:

DB DB DB

Теперь совпали все варианты: SQL, sql и Sql.

Что происходит, если совпадений нет

Если шаблон ничего не нашёл, REGEXP_REPLACE возвращает исходную строку без изменений.

SELECT REGEXP_REPLACE('hello', '[0-9]', '', 'g') AS result;

Результат:

hello

Цифр в строке нет, поэтому удалять нечего.

Это важный момент: функция не вернёт NULL только потому, что совпадений не было. Она просто оставит строку как есть.

А вот если сама исходная строка равна NULL, результат будет NULL:

SELECT REGEXP_REPLACE(NULL, '[0-9]', '', 'g') AS result;

Результат:

NULL

Если нужно считать NULL пустой строкой, используйте COALESCE:

SELECT REGEXP_REPLACE(COALESCE(phone, ''), '[^0-9]', '', 'g') AS digits_only
FROM users;

Обратные ссылки: как переставлять части строки

REGEXP_REPLACE умеет не только удалять и заменять текст. Она может захватывать части строки и использовать их в замене.

Для этого в регулярном выражении используют круглые скобки.

Например:

SELECT REGEXP_REPLACE('Ada Lovelace', '^(\w+)\s+(\w+)$', '\2, \1') AS result;

Результат:

Lovelace, Ada

Разберём шаблон '^(\w+)\s+(\w+)$':

  • ^ — начало строки;
  • (\w+) — первая группа: первое слово;
  • \s+ — один или несколько пробелов;
  • (\w+) — вторая группа: второе слово;
  • $ — конец строки.

Для строки:

Ada Lovelace

первая группа — это Ada, вторая группа — это Lovelace.

В замене мы пишем '\2, \1'.

Это значит: сначала подставь вторую группу, потом запятую и пробел, потом первую группу.

Получается:

Lovelace, Ada

Группы нумеруются слева направо

Порядок групп зависит от порядка открывающих скобок в шаблоне.

В шаблоне '^(\w+)\s+(\w+)$':

\1 -> первое слово
\2 -> второе слово

Если бы групп было три, появились бы \3, \4 и так далее.

Пример:

SELECT REGEXP_REPLACE('2026-06-19', '^([0-9]{4})-([0-9]{2})-([0-9]{2})$', '\3.\2.\1') AS result;

Результат:

19.06.2026

Группы такие:

\1 -> 2026
\2 -> 06
\3 -> 19

В замене мы переставили их в формат «день.месяц.год».

Форматирование телефона через группы

Допустим, у нас уже есть телефон только из цифр:

5551234567

Хотим получить красивый формат:

(555) 123-4567

Запрос:

SELECT REGEXP_REPLACE(
    '5551234567',
    '^([0-9]{3})([0-9]{3})([0-9]{4})$',
    '(\1) \2-\3'
) AS phone_pretty;

Результат:

(555) 123-4567

Разберём шаблон '^([0-9]{3})([0-9]{3})([0-9]{4})$':

  • первая группа ([0-9]{3}) — первые 3 цифры;
  • вторая группа ([0-9]{3}) — следующие 3 цифры;
  • третья группа ([0-9]{4}) — последние 4 цифры.

А замена '(\1) \2-\3' собирает номер в нужном виде.

Важно: такой запрос сработает только для строки ровно из 10 цифр. Если цифр больше или меньше, шаблон не совпадёт, и строка останется как есть.

Поэтому для реальной чистки телефона часто делают два шага:

WITH cleaned AS (
    SELECT
        phone,
        REGEXP_REPLACE(phone, '[^0-9]', '', 'g') AS digits
    FROM users
)
SELECT
    phone,
    digits,
    CASE
        WHEN digits ~ '^[0-9]{10}$'
            THEN REGEXP_REPLACE(digits, '^([0-9]{3})([0-9]{3})([0-9]{4})$', '(\1) \2-\3')
        ELSE digits
    END AS phone_pretty
FROM cleaned;

Сначала оставляем только цифры, потом форматируем только те строки, где получилось ровно 10 цифр.

Подстановка всего совпадения

В PostgreSQL в строке замены можно использовать не только \1, \2, \3, но и \&.

Запись \& означает всё найденное совпадение целиком.

Например, хотим обернуть все числа в квадратные скобки:

SELECT REGEXP_REPLACE('Order 42, user 105', '[0-9]+', '[\&]', 'g') AS result;

Результат:

Order [42], user [105]

Шаблон [0-9]+ нашёл числа 42 и 105.

А замена [\&] сказала: возьми всё найденное число и поставь вокруг него квадратные скобки.

Это удобно, когда не нужно разбивать совпадение на группы, а нужно просто обернуть или дополнить найденный фрагмент.

Многострочный текст и границы строк

В регулярных выражениях символы ^ и $ обычно означают начало и конец строки.

Например:

SELECT REGEXP_REPLACE('error: failed', '^error', 'warning') AS result;

Результат:

warning: failed

^error означает: слово error должно быть в начале строки.

Но если у вас в одной строке хранится многострочный текст, возникает вопрос: считать ли началом только начало всего текста или начало каждой строки внутри него?

В PostgreSQL для этого есть флаги, связанные с режимом обработки переводов строк. В практических статьях чаще всего достаточно запомнить идею: если вы работаете с многострочным текстом и используете ^ или $, обязательно проверьте поведение на примере с переводами строк.

Например, возьмите такой текст:

error: first
ok: second
error: third

И отдельно проверьте, заменяются ли error только в начале всего текста или в начале каждой строки.

Для большинства задач чистки телефонов, имён, email и пробелов многострочный режим не нужен. Он важен, когда вы обрабатываете большие текстовые поля, логи, сообщения или документы.

POSIX-классы символов

В PostgreSQL регулярные выражения используют POSIX-синтаксис. Поэтому можно писать классы символов в таком виде:

SELECT REGEXP_REPLACE('abc123', '[[:digit:]]', '', 'g');

Класс [[:digit:]] — это цифра, [[:alpha:]] — буква, [[:alnum:]] — буква или цифра.

Например, удалить всё, кроме букв и цифр:

SELECT REGEXP_REPLACE('User #42!', '[^[:alnum:]]', '', 'g') AS result;

Результат:

User42

Для новичка проще сначала использовать знакомые конструкции: [0-9], [^0-9], \s+, \w+.

Но POSIX-классы полезны, когда хочется написать более переносимый или более явный шаблон внутри PostgreSQL.

Частая ошибка: забыли флаг g

Самая частая ошибка с REGEXP_REPLACE в PostgreSQL — забыть флаг g.

Например:

SELECT REGEXP_REPLACE('a   b   c', '\s+', ' ') AS result;

Результат:

a b   c

Схлопнулась только первая группа пробелов.

Правильно:

SELECT REGEXP_REPLACE('a   b   c', '\s+', ' ', 'g') AS result;

Результат:

a b c

Если вы ожидаете, что заменятся все совпадения, почти всегда добавляйте g.

Частая ошибка: слишком широкий шаблон

Регулярные выражения мощные, но из-за этого ими легко удалить больше, чем нужно.

Например, кажется, что такой шаблон удалит HTML-теги:

SELECT REGEXP_REPLACE('<b>Hello</b> <i>SQL</i>', '<.*>', '', 'g') AS result;

Но результат может неприятно удивить:

Почему? Потому что .* может захватить слишком много: от первого < до последнего >.

Осторожнее можно написать так:

SELECT REGEXP_REPLACE('<b>Hello</b> <i>SQL</i>', '<[^>]*>', '', 'g') AS result;

Результат:

Hello SQL

Шаблон <[^>]*> означает:

  • < — открывающая угловая скобка;
  • [^>]* — любые символы, кроме закрывающей угловой скобки;
  • > — закрывающая угловая скобка.

Но даже это не полноценный HTML-парсер. Для сложного HTML лучше использовать специальные инструменты на уровне приложения. REGEXP_REPLACE подходит для простой технической чистки, а не для разбора сложных языков разметки.

Частая ошибка: регулярка вместо простой функции

Не нужно использовать REGEXP_REPLACE там, где достаточно обычного REPLACE.

Например, если нужно заменить все дефисы на пробелы:

SELECT REPLACE('a-b-c', '-', ' ') AS result;

Это проще и понятнее, чем:

SELECT REGEXP_REPLACE('a-b-c', '-', ' ', 'g') AS result;

Обычная замена быстрее читается и обычно проще для базы.

Простое правило:

Точная подстрока -> REPLACE
Шаблон или класс символов -> REGEXP_REPLACE

REGEXP_REPLACE и производительность

Регулярные выражения удобные, но не бесплатные. Базе нужно разобрать шаблон и проверить строку.

На маленьких таблицах это не страшно. Но на большой таблице запрос вида:

SELECT *
FROM users
WHERE REGEXP_REPLACE(phone, '[^0-9]', '', 'g') = '15551234567';

может быть тяжёлым.

Почему? Потому что база должна вычислить REGEXP_REPLACE(phone, ...) для множества строк, а обычный индекс по phone здесь обычно не поможет.

Лучше хранить нормализованное значение отдельно, если оно нужно для поиска постоянно.

Например:

ALTER TABLE users
ADD COLUMN phone_digits text;

Потом заполнить:

UPDATE users
SET phone_digits = REGEXP_REPLACE(phone, '[^0-9]', '', 'g');

И уже искать так:

SELECT *
FROM users
WHERE phone_digits = '15551234567';

А на phone_digits можно построить обычный индекс.

В PostgreSQL также можно использовать функциональный индекс по выражению, если вы действительно хотите искать именно так:

CREATE INDEX idx_users_phone_digits
ON users (REGEXP_REPLACE(phone, '[^0-9]', '', 'g'));

Но создавать такие индексы стоит только под реальные частые запросы.

REGEXP_REPLACE в CHECK и generated column

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

Например, если вам постоянно нужны только цифры телефона, можно сделать отдельную колонку или generated column.

Идея такая:

phone          -> как ввёл пользователь
phone_digits   -> только цифры

Так вы отделяете исходные данные от нормализованного вида.

Это особенно удобно, если:

  • по нормализованному телефону нужно искать;
  • нужно проверять уникальность;
  • данные приходят из разных источников;
  • отчёты и аналитика используют уже очищенное значение.

REGEXP_REPLACE отлично подходит для входа в такой пайплайн, но не всегда должен выполняться заново в каждом отчёте.

Разница между PostgreSQL, MySQL и ClickHouse

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

Но детали отличаются.

PostgreSQL

В PostgreSQL форма такая:

REGEXP_REPLACE(source, pattern, replacement [, flags]);

Пример:

SELECT REGEXP_REPLACE('+1 (555) 123-45-67', '[^0-9]', '', 'g') AS digits;

Результат:

15551234567

Главное:

  • без g заменяется только первое совпадение;
  • g включает замену всех совпадений;
  • i включает поиск без учёта регистра;
  • обратные ссылки в замене пишутся как \1, \2, \3;
  • \& означает всё совпадение целиком.

MySQL

В MySQL 8+ функция тоже называется REGEXP_REPLACE, но сигнатура другая:

REGEXP_REPLACE(expr, pattern, replacement [, pos [, occurrence [, match_type]]]);

Пример удаления всего, кроме цифр:

SELECT REGEXP_REPLACE(phone, '[^0-9]', '') AS digits
FROM users;

В MySQL глобальная замена обычно идёт по умолчанию, потому что occurrence = 0 означает заменить все совпадения.

Если нужно явно указать режим без учёта регистра, используют match_type:

SELECT REGEXP_REPLACE(email, 'gmail', 'gmail', 1, 0, 'i') AS normalized_email
FROM users;

Здесь:

  • 1 — начинать поиск с первой позиции;
  • 0 — заменить все совпадения;
  • 'i' — искать без учёта регистра.

С обратными ссылками в MySQL будьте особенно внимательны: синтаксис и экранирование могут отличаться от PostgreSQL. В примерах и окружениях часто встречается формат $1, $2, а в строковых литералах с обратными слэшами может понадобиться дополнительное экранирование. Перед переносом запроса с группами обязательно проверьте его на маленьком примере.

ClickHouse

В ClickHouse для регулярной замены часто используют две функции: replaceRegexpOne и replaceRegexpAll.

replaceRegexpOne заменяет первое совпадение.

replaceRegexpAll заменяет все совпадения.

Пример:

SELECT replaceRegexpAll(phone, '[^0-9]', '') AS digits
FROM users;

Если нужно заменить только первое совпадение:

SELECT replaceRegexpOne('a-b-c', '-', '+') AS result;

Результат:

a+b-c

Если нужно заменить все:

SELECT replaceRegexpAll('a-b-c', '-', '+') AS result;

Результат:

a+b+c

В ClickHouse глобальность видна прямо из имени функции. Это удобно: не нужно помнить про флаг g, как в PostgreSQL.

Что тестировать перед переносом между СУБД

Если вы переносите регулярную замену между PostgreSQL, MySQL и ClickHouse, не проверяйте её только на одной красивой строке.

Соберите маленький набор тестов:

+1 (555) 123-45-67
555 123 45 67
hello   world
SQL sql Sql
Ada Lovelace

NULL

И проверьте:

  • что происходит с пустой строкой;
  • что происходит с NULL;
  • заменяется первое совпадение или все;
  • как включается поиск без учёта регистра;
  • как работают группы;
  • как пишутся обратные ссылки;
  • нужно ли экранировать обратные слэши;
  • не удаляет ли шаблон лишний текст.

Особенно осторожно переносите запросы с группами и обратными ссылками. Именно там различия между СУБД чаще всего приводят к неожиданному результату.

Практический пример: чистим телефон и проверяем длину

Допустим, телефоны должны храниться в любом виде, но для проверки нам нужны только цифры.

SELECT
    id,
    phone,
    REGEXP_REPLACE(phone, '[^0-9]', '', 'g') AS digits
FROM users;

Результат:

id | phone                | digits
---+----------------------+-------------
1  | +1 (555) 123-45-67   | 15551234567
2  | 555 123 45 67        | 5551234567
3  | abc                  |

Теперь можно найти подозрительные телефоны:

SELECT
    id,
    phone,
    REGEXP_REPLACE(phone, '[^0-9]', '', 'g') AS digits
FROM users
WHERE char_length(REGEXP_REPLACE(phone, '[^0-9]', '', 'g')) NOT BETWEEN 10 AND 15;

Такой запрос полезен для аудита данных.

Но если такая проверка нужна постоянно, лучше вынести очищенный телефон в отдельную колонку, чтобы не считать регулярку каждый раз.

Практический пример: нормализуем пробелы в именах

SELECT
    id,
    name AS old_name,
    TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g')) AS new_name
FROM users
WHERE name <> TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'));

Так можно найти строки, где есть лишние пробелы.

После проверки:

UPDATE users
SET name = TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'))
WHERE name <> TRIM(REGEXP_REPLACE(name, '\s+', ' ', 'g'));

Это нормальный сценарий для разовой чистки данных после импорта.

Практический пример: меняем формат даты

Есть строка:

2026-06-19

Хотим получить:

19.06.2026

Запрос:

SELECT REGEXP_REPLACE(
    '2026-06-19',
    '^([0-9]{4})-([0-9]{2})-([0-9]{2})$',
    '\3.\2.\1'
) AS formatted_date;

Результат:

19.06.2026

Этот пример хорошо показывает силу групп: мы не просто заменили текст, а переставили части строки местами.

Но если в колонке хранится настоящий тип date, лучше форматировать дату средствами работы с датами, а не регулярками. Регулярные выражения здесь уместны только если дата пришла как текст.

Практический пример: удалить всё, кроме букв и цифр

Допустим, нужно получить простой код из строки:

User #42!

Оставим только буквы и цифры:

SELECT REGEXP_REPLACE('User #42!', '[^[:alnum:]]', '', 'g') AS code;

Результат:

User42

Такой приём используют для технических кодов, slug-подготовки или очистки импортированных значений.

Но для полноценной генерации URL-slug обычно нужна более сложная логика: транслитерация, нижний регистр, замена пробелов на дефисы, обработка дублей. Одного REGEXP_REPLACE может быть мало.

Коротко

REGEXP_REPLACE — это функция для замены текста по регулярному выражению.

В PostgreSQL базовый синтаксис такой:

REGEXP_REPLACE(source, pattern, replacement [, flags]);

Пример:

SELECT REGEXP_REPLACE('+1 (555) 123-45-67', '[^0-9]', '', 'g');

Результат:

15551234567

Главные правила:

  • REGEXP_REPLACE ищет не точную строку, а шаблон;
  • для точной замены проще использовать обычный REPLACE;
  • в PostgreSQL без флага g заменяется только первое совпадение;
  • флаг g заменяет все совпадения;
  • флаг i включает поиск без учёта регистра;
  • пустая строка '' в замене удаляет найденный текст;
  • если совпадений нет, исходная строка возвращается без изменений;
  • если исходная строка NULL, результат тоже NULL;
  • группы в круглых скобках можно использовать в замене через \1, \2, \3;
  • \& в PostgreSQL означает всё совпадение целиком;
  • регулярные выражения удобно использовать для чистки телефонов, пробелов, кодов и текстовых импортов;
  • для больших таблиц регулярки в WHERE могут быть дорогими;
  • часто используемые нормализованные значения лучше хранить отдельно или индексировать выражение;
  • при переносе между PostgreSQL, MySQL и ClickHouse проверяйте флаги, глобальность, экранирование и обратные ссылки.

Самое простое правило: если нужно заменить конкретный кусок текста — используйте REPLACE. Если нужно заменить «все цифры», «все лишние пробелы», «всё, кроме букв», «любой вариант регистра» или переставить части строки по группам — используйте REGEXP_REPLACE.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр