Конкатенация строк
Склеивание текста в PostgreSQL
Теория
• показать полное имя пользователя из имени и фамилии;
• собрать подпись вида 'Москва / gmail.com';
• сделать код товара из кусочков названия, категории и идентификатора;
• подготовить более удобный для чтения текст прямо в результате запроса.
Такое склеивание строк и называется конкатенацией. У слова «конкатенация», кстати, есть много похожих по смыслу определений: сцепление, соединение и так далее.
Так вот, в контексте PostgreSQL конкатенация — это когда мы берём несколько текстовых фрагментов и соединяем их в одну строку. Для этого есть несколько способов:
• функция CONCAT();
• функция CONCAT_WS();
• оператор ||.
На первый взгляд они делают одно и то же, но на практике между ними есть важная разница, особенно когда в одном из фрагментов встречается NULL.
Достаточно часто при конкатенации вы будете использовать уже знакомые вам инструменты:
• строковые функции вроде LEFT, RIGHT, UPPER;
• подстановка через COALESCE;
• условная логика через CASE;
Синтаксис
SELECT CONCAT(first_name, ' ', last_name),
CONCAT_WS(' / ', city, email),
first_name || ' ' || last_name
FROM table_name;
SQL
Что важно понимать по каждому из них:
• CONCAT(part1, part2, part3) — просто соединяет части друг за другом;
• CONCAT(part1,'разделитель', part2,'разделитель', part3) — соединяет части друг за другом и между ними вставляет разделитель;
• CONCAT_WS('разделитель', part1, part2, part3) — тоже склеивает части, но автоматически вставляет между ними указанный в самом начале разделитель;
• part1 || part2 — оператор склеивания строк, делает тоже самое, но есть но...
Теперь, про то самое но:
• CONCAT в PostgreSQL спокойно переживает NULL и просто пропускает такой фрагмент;
• CONCAT_WS тоже пропускает NULL, а разделитель ставит только между реальными значениями;
• оператор || ведёт себя строже: если одна из частей при конкатенации равна NULL, весь результат склейки станет NULL.
Это очень важная разница. Например:
• CONCAT('SQL', NULL, 'PRO') вернёт 'SQLPRO';
• 'SQL' || NULL || 'PRO' вернёт NULL.
Когда и что использовать:
• CONCAT удобно, когда фрагментов немного или когда между ними нужны разные разделители;
• CONCAT_WS особенно хорош, когда нужно склеить много частей через один и тот же разделитель;
• CONCAT_WS не подойдет, если между разными частями должны стоять разные разделители;
• || удобен для коротких и простых выражений, но с ним нужно внимательнее следить за NULL.
Ещё одна особенность PostgreSQL: CONCAT и CONCAT_WS сами умеют приводить многие значения к тексту. Поэтому, если вы склеиваете строку, число или дату, дополнительно переводить тип данных ::text часто вообще не нужно. Но если вы хотите явно контролировать формат значения, приведение типа явным образом не будет лишним.
Как упоминалось выше в конкатенацию можно спокойно вкладывать уже знакомые функции. Например, запись CONCAT(UPPER(LEFT(city, 3)), '-', user_id) сначала возьмёт первые три буквы города, переведёт их в верхний регистр, а потом склеит с дефисом и идентификатором.
Примеры
1. Собираем полное имя пользователя
🔄 Попробуйте изменить запрос:
- • Замените пробел на ' - ' и посмотрите, как изменится результат
- • Попробуйте тот же сценарий через оператор ||
2. Склеиваем сразу несколько столбцов через один разделитель
🔄 Попробуйте изменить запрос:
- • Перепишите этот же запрос через обычный CONCAT и сравните, насколько запись станет длиннее
- • Добавьте в склейку ещё и registration_date и посмотрите, что PostgreSQL сам приведёт дату к тексту
3. Сравниваем поведение CONCAT и оператора || при NULL
🔄 Попробуйте изменить запрос:
- • Замените NULL на '-' и сравните оба результата
- • Попробуйте защитить оператор || через COALESCE(NULL, '')
4. Используем CONCAT, когда разделители между частями разные
🔄 Попробуйте изменить запрос:
- • Замените квадратные скобки на круглые: '(' и ')'
- • Добавьте в конец строки ещё и email, отделив его через ' - '
4. Собираем условный шифр товара из нескольких кусков
🔄 Попробуйте изменить запрос:
- • Сделайте код короче: возьмите не четыре, а три символа из product_name
- • Поменяйте разделитель '-' на '_'
6. Собираем шифр заказа через оператор || и защищаемся от NULL
🔄 Попробуйте изменить запрос:
- • Уберите функцию COALESCE и посмотрите, что произойдёт у заказов без статуса
- • Добавьте в шифр ещё и worker_id
Типичные ошибки
Считают, что CONCAT и оператор || всегда ведут себя одинаково
Нет, не всегда. При NULL это как раз не так. CONCAT спокойно пропускает пустой фрагмент, а || может вернуть NULL для всей склейки.
Забывают, что у CONCAT_WS первый аргумент — это разделитель
В записи CONCAT_WS(' / ', city, email) строка ' / ' — это не обычная часть результата, а именно разделитель между остальными значениями.
Лепят разделители вручную там, где лучше подошёл бы CONCAT_WS
Если у вас несколько частей и между ними нужен один и тот же разделитель, CONCAT_WS обычно делает запись чище и надёжнее.
Думают, что число или дата обязательно требуют ::text в CONCAT
Во многих обычных случаях PostgreSQL сам приведёт такие значения к тексту внутри CONCAT и CONCAT_WS. Явное приведение нужно не всегда, а только когда вы хотите жёстко контролировать формат или работаете в более чувствительном сценарии.
Забывают защититься от NULL при использовании ||
Если в одной из частей может быть NULL, рядом часто нужен COALESCE. Например: first_name || ' ' || COALESCE(city, '').
Пытаются собрать сложный код без промежуточной логики
Когда в строку нужно включить куски разных полей, почти всегда удобнее сначала применить LEFT, RIGHT, SUBSTRING, UPPER или другие функции, а уже потом делать конкатенацию.
Практика
Проверь себя
Ответьте на вопросы, чтобы закрепить материал: