SQLCONCATstringtutorial

Что такое CONCAT в SQL?

CONCAT — это «склей строки в одну». Простыми словами: три формы (||, CONCAT, CONCAT_WS), как они по-разному обращаются с NULL и почему CONCAT_WS — лучший выбор для адресов и имён. С таблицами и частыми ошибками.

10 мин чтенияСправочникSQL · CONCAT · string · tutorial

CONCAT в SQL — это способ склеить несколько значений в одну строку.

Например, у нас есть имя и фамилия в разных колонках:

  • имя: Анна
  • фамилия: Петрова

А на сайте нужно показать красиво:

Анна Петрова

Вот для этого и нужна конкатенация: она берёт кусочки текста и собирает из них одну готовую строку.

На первый взгляд всё просто: «ну что там, склеить две строки». Но в SQL есть важная ловушка: разные способы склейки по-разному ведут себя с NULL. А NULL в базе встречается часто: у пользователя может не быть отчества, у адреса — квартиры, у товара — описания.

Из-за этого легко получить неприятный баг: хотели вывести полное имя, а у части пользователей вместо имени внезапно получилось NULL.

Разберём спокойно и по-человечески:

  • оператор ||;
  • функцию CONCAT;
  • функцию CONCAT_WS;
  • чем они отличаются;
  • почему для имён и адресов чаще всего лучше брать CONCAT_WS.

Зачем вообще склеивать строки в SQL

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

Типичные примеры:

  • собрать полное имя из имени, отчества и фамилии;
  • собрать адрес из улицы, квартиры, города и страны;
  • собрать ссылку из домена и пути;
  • сформировать подпись, сообщение, slug или строку для отчёта;
  • вывести текст вроде Order #42.

Допустим, есть таблица пользователей:

id first_name last_name
1 Anna Petrova
2 Boris Sidorov

Можно получить полное имя прямо в запросе:

SELECT
  first_name,
  last_name,
  CONCAT(first_name, ' ', last_name) AS full_name
FROM users;

Результат:

first_name last_name full_name
Anna Petrova Anna Petrova
Boris Sidorov Boris Sidorov

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

Способ 1. Оператор ||

Оператор || — это стандартный способ конкатенации в SQL.

Пример:

SELECT 'Hello, ' || 'World' AS message;

Результат:

message
Hello, World

Можно склеивать не только две строки, а сколько угодно частей:

SELECT 'User: ' || first_name || ' ' || last_name AS user_label
FROM users;

Получится что-то вроде:

user_label
User: Anna Petrova
User: Boris Sidorov

Оператор || работает, например, в PostgreSQL, SQLite, Oracle и DB2.

Но есть важный момент: в MySQL по умолчанию || не склеивает строки. Там он воспринимается как логическое OR. Поэтому если вы пишете запросы под MySQL, для склейки строк обычно используют CONCAT.

Главная ловушка оператора || — NULL

Вот тут начинается самое важное.

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

SELECT 'Hello, ' || NULL AS message;

Результат будет:

message
NULL

Почему так?

Потому что NULL в SQL означает не «пустая строка», а неизвестное значение.

SQL рассуждает примерно так:

Я знаю первую часть строки: Hello, .
Но вторая часть неизвестна.
Значит, весь результат тоже неизвестен.

То же самое произойдёт, если NULL окажется где-то в середине:

SELECT 'Hello, ' || NULL || '!' AS message;

Результат:

message
NULL

Один NULL ломает всю строку.

На практике это часто всплывает с отчеством, квартирой, дополнительной строкой адреса, комментарием или любым необязательным полем.

Как защититься через COALESCE

Если вы всё-таки используете ||, можно заранее заменить NULL на запасное значение через COALESCE.

Например:

SELECT
  'Hello, ' || COALESCE(name, 'Guest') AS message
FROM users;

Если name есть, будет имя. Если name равен NULL, подставится Guest.

Для полного имени можно написать так:

SELECT
  COALESCE(first_name, '') || ' ' || COALESCE(last_name, '') AS full_name
FROM users;

Но у этого подхода есть минус: легко получить лишние пробелы.

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

full_name
Anna

Визуально после Anna может остаться лишний пробел. С именами это ещё терпимо, а вот с адресами начинаются двойные пробелы, лишние запятые и некрасивые строки.

Способ 2. CONCAT

CONCAT — это функция для склейки строк.

Пример:

SELECT CONCAT('Hello, ', 'World') AS message;

Результат:

message
Hello, World

Можно передать больше двух аргументов:

SELECT CONCAT('a', 'b', 'c', 'd') AS result;

Результат:

result
abcd

Для полного имени:

SELECT
  CONCAT(first_name, ' ', last_name) AS full_name
FROM users;

CONCAT есть в PostgreSQL, MySQL и SQL Server. В SQLite чаще используют оператор ||, хотя в некоторых версиях и сборках может быть доступна функция concat.

Главное отличие CONCAT от || — поведение с NULL.

Как CONCAT работает с NULL

Посмотрим:

SELECT CONCAT('Hello, ', NULL, '!') AS message;

Результат:

message
Hello, !

CONCAT обычно воспринимает NULL как пустую строку. То есть он не ломает весь результат, а просто пропускает неизвестное значение как пустое место.

Это удобно.

Но не идеально.

Например, соберём имя, отчество и фамилию:

SELECT
  CONCAT(first_name, ' ', middle_name, ' ', last_name) AS full_name
FROM users;

Если middle_name равен NULL, результат может получиться таким:

full_name
Boris Sidorov

Обратите внимание: между Boris и Sidorov два пробела.

Почему?

Потому что CONCAT пропустил само значение middle_name, но пробелы вокруг него остались:

first_name + ' ' + NULL + ' ' + last_name

NULL исчез, а два разделителя остались.

Именно поэтому для имён, адресов и похожих задач часто лучше подходит не просто CONCAT, а CONCAT_WS.

Способ 3. CONCAT_WS

CONCAT_WS расшифровывается как concat with separator — «склеить с разделителем».

Первый аргумент — это разделитель. Остальные аргументы — значения, которые нужно склеить.

Пример:

SELECT CONCAT_WS(', ', 'Anna', 'Moscow', 'Russia') AS address;

Результат:

address
Anna, Moscow, Russia

Разделитель , автоматически вставляется между значениями.

Теперь самое приятное: CONCAT_WS пропускает NULL и не ставит лишние разделители.

SELECT CONCAT_WS(', ', 'Anna', NULL, 'Russia') AS address;

Результат:

address
Anna, Russia

Без двойных запятых. Без дырок. Без ручного CASE.

Это и есть главная сила CONCAT_WS.

CONCAT_WS для полного имени

Допустим, у пользователя может быть отчество, а может не быть.

Плохая новость: если склеивать через ||, можно получить NULL.

Средняя новость: если склеивать через CONCAT, можно получить лишние пробелы.

Хорошая новость: CONCAT_WS решает задачу аккуратно.

SELECT
  CONCAT_WS(' ', first_name, middle_name, last_name) AS full_name
FROM users;

Если все три части есть:

full_name
Anna Ivanovna Petrova

Если отчества нет:

full_name
Boris Sidorov

Никакого двойного пробела.

CONCAT_WS для адреса

С адресами ситуация ещё показательнее.

Адрес часто состоит из нескольких частей:

  • улица;
  • дом;
  • квартира;
  • город;
  • страна.

Но квартира может быть не указана. Район может быть не указан. Индекс может быть не указан.

Вот хороший вариант:

SELECT
  CONCAT_WS(', ', street, apartment, city, country) AS full_address
FROM addresses;

Если все поля заполнены:

full_address
Main St, Apt 12, Boston, USA

Если квартиры нет:

full_address
Main St, Boston, USA

CONCAT_WS не оставляет строку вида:

Main St, , Boston, USA

Именно поэтому его так любят для адресов, имён и человекочитаемых строк.

Сравним три способа на одной таблице

Пусть есть таблица users:

id first_name middle_name last_name
1 Anna Ivanovna Petrova
2 Boris NULL Sidorov
3 NULL NULL NULL

Сравним ||, CONCAT и CONCAT_WS:

SELECT
  id,
  first_name || ' ' || middle_name || ' ' || last_name AS via_pipes,
  CONCAT(first_name, ' ', middle_name, ' ', last_name) AS via_concat,
  CONCAT_WS(' ', first_name, middle_name, last_name) AS via_concat_ws
FROM users;

Результат будет примерно таким:

id via_pipes via_concat via_concat_ws
1 Anna Ivanovna Petrova Anna Ivanovna Petrova Anna Ivanovna Petrova
2 NULL Boris Sidorov Boris Sidorov
3 NULL

Что здесь произошло:

  • || сломался на строке с Борисом, потому что middle_name равен NULL;
  • CONCAT не сломался, но оставил двойной пробел;
  • CONCAT_WS дал самый аккуратный результат.

Поэтому для полного имени почти всегда лучше использовать:

SELECT
  CONCAT_WS(' ', first_name, middle_name, last_name) AS full_name
FROM users;

А для адреса:

SELECT
  CONCAT_WS(', ', street, apartment, city, country) AS full_address
FROM addresses;

CONCAT и числа

Конкатенация умеет работать не только со строками. Числа и даты часто автоматически приводятся к тексту.

Например:

SELECT CONCAT('Order #', 42) AS label;

Результат:

label
Order #42

Ещё пример:

SELECT CONCAT_WS('-', 2024, 12, 31) AS date_text;

Результат:

date_text
2024-12-31

Это удобно, но есть нюанс: автоматическое преобразование не всегда даёт тот формат, который вы хотите.

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

Если формат важен, лучше явно управлять преобразованием.

В PostgreSQL можно так:

SELECT
  'Price: ' || price::TEXT AS price_label
FROM products;

Для даты часто используют форматирование:

SELECT
  'Date: ' || TO_CHAR(created_at, 'YYYY-MM-DD') AS created_label
FROM orders;

Так вы явно говорите базе: «покажи дату именно в таком виде».

Конкатенация в WHERE: почему нужно быть осторожным

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

Но есть важный случай: не стоит бездумно склеивать поля в WHERE.

Например:

SELECT *
FROM users
WHERE first_name || ' ' || last_name = 'Anna Petrova';

Запрос выглядит понятно, но для базы это может быть неудобно.

Если у вас есть отдельные индексы по first_name и last_name, выражение со склейкой может помешать использовать их эффективно. Базе приходится сначала собрать строку, а потом сравнить её с нужным значением.

Чаще лучше написать условие по отдельным колонкам:

SELECT *
FROM users
WHERE first_name = 'Anna'
  AND last_name = 'Petrova';

Так базе проще использовать индексы.

Если же полное имя реально часто нужно искать или сортировать как одну строку, можно завести отдельную колонку full_name, например вычисляемую, и индексировать уже её.

Конкатенация в ORDER BY

Похожая история с сортировкой.

Можно написать так:

SELECT
  id,
  CONCAT_WS(' ', first_name, last_name) AS full_name
FROM users
ORDER BY CONCAT_WS(' ', first_name, last_name);

Для небольших таблиц это нормально.

Но если таблица большая и такая сортировка используется часто, лучше подумать о готовом поле или функциональном индексе, если ваш диалект SQL это поддерживает.

Главная мысль простая: собрать красивую строку для вывода — нормально. Но если вы по этой строке постоянно ищете, фильтруете и сортируете, возможно, она заслуживает отдельного места в структуре данных.

Частые ошибки новичков

Ошибка 1. Использовать || и забыть про NULL

Пишут так:

SELECT
  first_name || ' ' || middle_name || ' ' || last_name AS full_name
FROM users;

А потом удивляются, почему у пользователя без отчества полное имя стало NULL.

Если поле может быть пустым, используйте COALESCE, CONCAT или лучше CONCAT_WS.

Ошибка 2. Использовать || в MySQL

В PostgreSQL такой запрос склеит строки:

SELECT 'Hello, ' || 'World' AS message;

А в MySQL по умолчанию || — это логическое OR, а не конкатенация.

Для MySQL используйте:

SELECT CONCAT('Hello, ', 'World') AS message;

Если пишете код, который должен быть понятен в разных SQL-диалектах, CONCAT часто безопаснее, чем ||.

Ошибка 3. Получить лишние пробелы через CONCAT

Вот такой запрос не сломается на NULL:

SELECT
  CONCAT(first_name, ' ', middle_name, ' ', last_name) AS full_name
FROM users;

Но если middle_name равен NULL, можно получить двойной пробел.

Для полного имени лучше так:

SELECT
  CONCAT_WS(' ', first_name, middle_name, last_name) AS full_name
FROM users;

Ошибка 4. Получить лишние запятые в адресе

Новичок часто пишет:

SELECT
  CONCAT(street, ', ', apartment, ', ', city, ', ', country) AS full_address
FROM addresses;

Если apartment равен NULL, результат может выглядеть некрасиво:

Main St, , Boston, USA

Лучше:

SELECT
  CONCAT_WS(', ', street, apartment, city, country) AS full_address
FROM addresses;

Так NULL будет пропущен вместе с лишней запятой.

Ошибка 5. Думать, что конкатенация и сложение — одно и то же

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

Например, в SQL Server оператор + может использоваться для строк:

SELECT 'Hello, ' + 'World' AS message;

Но с числами + — это уже сложение:

SELECT 42 + 7 AS result;

Результат будет 49, а не 427.

Поэтому для строк лучше использовать понятные строковые функции, например CONCAT, особенно если вы только учитесь и не хотите путаться в особенностях диалектов.

Ошибка 6. Делать CSV через простую склейку

Иногда кажется, что CSV можно собрать так:

SELECT
  CONCAT_WS(', ', name, city, country) AS csv_line
FROM users;

Для простого отображения это нормально. Но для настоящего CSV-экспорта опасно.

Если внутри значения уже есть запятая, строка может сломаться для CSV-парсера.

Например:

name city country
Anna, friend Moscow Russia

Простая склейка даст:

Anna, friend, Moscow, Russia

Выглядит как четыре поля вместо трёх.

Для настоящего CSV нужны экранирование, кавычки и нормальный экспортный механизм, а не просто CONCAT_WS.

Как выбрать правильный способ

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

Задача Что использовать
Склеить строки в PostgreSQL или SQLite `
Склеить строки более универсально CONCAT
Склеить поля с разделителем CONCAT_WS
Собрать полное имя CONCAT_WS
Собрать адрес CONCAT_WS
Защититься от NULL без лишних разделителей CONCAT_WS
Сделать строгий формат даты или числа явное форматирование

Если сомневаетесь, запомните практическую формулу:

Для человекочитаемых строк с пробелами, запятыми и необязательными полями чаще всего нужен CONCAT_WS.

Главное

Конкатенация в SQL — это склейка нескольких значений в одну строку.

Есть три популярных способа:

  1. || — стандартный SQL-оператор, но один NULL превращает весь результат в NULL.
  2. CONCAT — функция склейки, которая обычно воспринимает NULL как пустую строку.
  3. CONCAT_WS — склейка с разделителем, которая пропускает NULL и не оставляет лишние пробелы или запятые.

Для простых строк подойдёт CONCAT.

Для имён, адресов, подписей и других строк, где часть полей может быть NULL, чаще всего лучший выбор — CONCAT_WS.

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

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

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

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