Строковые функции SQL с примерами
Все задачи по работе со строками в SQL сводятся к четырём действиям: склеить, вырезать, почистить и найти. Шпаргалка по основным функциям:
Разберём каждую группу на примерах из базы данных сервиса доставки — все запросы можно выполнять прямо в статье.
Склеить: CONCAT и CONCAT_WS
CONCAT объединяет любое число строк в одну:
MySQL 8.1SELECT CONCAT(name, ' — ', category) AS title FROM products ORDER BY product_id LIMIT 3;
Когда разделитель один и тот же, удобнее CONCAT_WS («with separator»): первый аргумент — разделитель, дальше — склеиваемые части:
MySQL 8.1SELECT CONCAT_WS(', ', country, acquisition_channel) FROM users; -- 'Germany, Ads'
В PostgreSQL и стандартном SQL есть ещё оператор ||: name || ' — ' || category. В MySQL по умолчанию || означает логическое ИЛИ, поэтому в переносимом коде надёжнее CONCAT.
Ловушка NULL в конкатенации
На NULL функции склейки ведут себя по-разному, и это популярный источник пустых строк в отчётах:
- MySQL: CONCAT('a', NULL, 'b') возвращает NULL — одно пустое значение «отравляет» весь результат.
- PostgreSQL: CONCAT('a', NULL, 'b') возвращает ab — NULL просто пропускается. Но оператор || ведёт себя как MySQL: 'a' || NULL даёт NULL.
Если в склеиваемых столбцах возможны пропуски, оберните их в COALESCE — или используйте CONCAT_WS, который игнорирует NULL в обеих СУБД.
Что вернёт CONCAT('Заказ №', order_id, ' от ', promo_code) в MySQL, если promo_code равен NULL?
Склеить группу строк: GROUP_CONCAT и STRING_AGG
Отдельная суперсила — собрать в одну строку значения из нескольких строк таблицы. Соберём список товаров каждой категории:
В MySQL это делает GROUP_CONCAT:
MySQL 8.1SELECT category, GROUP_CONCAT(name ORDER BY name SEPARATOR ', ') AS product_list FROM products GROUP BY category ORDER BY category;
В PostgreSQL ту же задачу решает STRING_AGG:
PostgreSQL 17.5SELECT category, STRING_AGG(name, ', ' ORDER BY name) AS product_list FROM products GROUP BY category ORDER BY category;
Функции называются по-разному — GROUP_CONCAT в MySQL, STRING_AGG в PostgreSQL, — но делают одно и то же. Это агрегатные функции, как SUM или COUNT, поэтому работают вместе с GROUP BY.
Какая функция соберёт значения из нескольких строк таблицы в один список через запятую в PostgreSQL?
Вырезать: SUBSTRING, LEFT, RIGHT
SUBSTRING(строка, позиция, длина) вырезает фрагмент строки; нумерация символов начинается с единицы. В связке с POSITION, которая находит позицию подстроки, получаются практичные приёмы — например, выделить домен из email:
MySQL 8.1SELECT email, SUBSTRING(email, POSITION('@' IN email) + 1) AS domain FROM users ORDER BY user_id LIMIT 3;
POSITION('@' IN email) возвращает номер позиции символа @, а SUBSTRING без третьего аргумента забирает всё от этой позиции до конца строки. Попробуйте сами: вытащите из email логин — часть до символа @. Понадобится третий аргумент и POSITION('@' IN email) - 1.
Для краёв строки есть сокращения: LEFT(строка, n) — первые n символов, RIGHT(строка, n) — последние n.
Почистить: TRIM и REPLACE
TRIM убирает пробелы с обоих концов строки — первая помощь при работе с данными, введёнными людьми:
MySQL 8.1SELECT TRIM(' SUMMER20 '); -- 'SUMMER20'
REPLACE(строка, что_искать, на_что_заменить) заменяет все вхождения подстроки:
MySQL 8.1SELECT phone, REPLACE(REPLACE(phone, '-', ''), ' ', '') AS normalized FROM users WHERE user_id IN (1, 4) ORDER BY user_id;
Вложенные REPLACE — обычный приём для нормализации телефонов и артикулов перед сравнением. Для замен по шаблону, а не по точной подстроке, существует REGEXP_REPLACE — про шаблоны мы подробно рассказываем в уроке Оператор REGEXP.
Частный случай очистки — пустые строки и строки из пробелов вместо NULL. Эту задачу решает связка COALESCE(NULLIF(TRIM(...), ''), ...), которую мы разобрали в статье про COALESCE.
Регистр и длина: UPPER, LOWER, LENGTH
UPPER и LOWER переводят строку в верхний и нижний регистр. Классическое применение — сравнение без учёта регистра: LOWER(email) = LOWER('User@Mail.com').
С длиной строки есть нюанс, который стоит знать заранее:
- MySQL: LENGTH() считает байты, а CHAR_LENGTH() — символы. Для латиницы это одно и то же, но кириллица в UTF-8 занимает два байта на букву: LENGTH('привет') вернёт 12, а CHAR_LENGTH('привет') — 6.
- PostgreSQL: LENGTH() сразу считает символы — LENGTH('привет') вернёт 6.
Если нужна длина текста с точки зрения человека — используйте CHAR_LENGTH: она работает одинаково в обеих СУБД.
Различия MySQL и PostgreSQL: сводная таблица
Что дальше
- как устроен строковый тип данных — в уроке Строковый тип данных;
- поиск по шаблону — в уроках Оператор LIKE и Оператор REGEXP;
- быстрые справки по функциям — в справочнике: CONCAT, SUBSTRING, TRIM, REPLACE, GROUP_CONCAT;
- потренироваться на задачах со строками — в тренажёре SQL.
Читайте по теме
ROW_NUMBER, RANK и DENSE_RANK в SQL: отличия на одном примере
Три ранжирующие функции, один запрос — и разница видна
COALESCE в SQL: что это и как работает — примеры | SQL Academy
Первый не-NULL аргумент, и зачем рядом NULLIF
CTE в SQL: что такое Common Table Expression (WITH) — примеры
Подзапросы с именами, цепочки шагов и рекурсия