Как разбить строку в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это нужно аналитику

Разбить строку — базовая задача при работе с грязными данными. В колонке full_name лежит 'Иван Иванов' — нужно разделить на first и last. В email-е 'alice@example.com' хотите извлечь домен. В URL 'https://site.ru?utm_source=google&utm_medium=cpc' — достать UTM.

На собесе часто спрашивают: «разбей полное имя на first / last в SQL». Junior ответит «не знаю». Middle — SPLIT_PART в Postgres или SUBSTRING_INDEX в MySQL. Senior обсудит нюансы (что если больше двух слов, что если есть null).

В статье — все способы разбиения:

  • SPLIT_PART в Postgres
  • SUBSTRING_INDEX в MySQL
  • STRING_TO_ARRAY + unnest (Postgres)
  • splitByChar в ClickHouse
  • SUBSTRING для фиксированных позиций

1. SPLIT_PART в Postgres

Взять N-ю часть по разделителю:

SELECT SPLIT_PART('Иван Иванов Иванович', ' ', 1);  -- 'Иван'
SELECT SPLIT_PART('Иван Иванов Иванович', ' ', 2);  -- 'Иванов'
SELECT SPLIT_PART('Иван Иванов Иванович', ' ', 3);  -- 'Иванович'
SELECT SPLIT_PART('alice@example.com', '@', 2);     -- 'example.com'

Если части N не существует — возвращает пустую строку.

2. SUBSTRING_INDEX в MySQL

-- взять часть до 1-го пробела
SELECT SUBSTRING_INDEX('Иван Иванов Иванович', ' ', 1);   -- 'Иван'

-- до 2-го пробела
SELECT SUBSTRING_INDEX('Иван Иванов Иванович', ' ', 2);   -- 'Иван Иванов'

-- от конца (отрицательный)
SELECT SUBSTRING_INDEX('Иван Иванов Иванович', ' ', -1);  -- 'Иванович'

-- домен email (после @)
SELECT SUBSTRING_INDEX('alice@example.com', '@', -1);     -- 'example.com'

3. STRING_TO_ARRAY в Postgres

Превратить строку в массив:

SELECT STRING_TO_ARRAY('Иван Иванов Иванович', ' ');
-- {Иван, Иванов, Иванович}

-- по индексу
SELECT (STRING_TO_ARRAY('a,b,c,d', ','))[2];  -- 'b'

-- развернуть в rows
SELECT unnest(STRING_TO_ARRAY('a,b,c', ','));
-- a
-- b
-- c

4. REGEXP_SPLIT_TO_ARRAY (Postgres)

По regex:

-- по любому пробельному (пробел, таб, перевод строки)
SELECT REGEXP_SPLIT_TO_ARRAY('Иван  Иванов   Иванович', '\s+');
-- {Иван, Иванов, Иванович}

5. splitByChar в ClickHouse

SELECT splitByChar(' ', 'Иван Иванов Иванович');
-- ['Иван', 'Иванов', 'Иванович']

-- по индексу
SELECT arrayElement(splitByChar(',', 'a,b,c'), 2);  -- 'b'

6. Практика: разбить full_name

-- Postgres
SELECT
    full_name,
    SPLIT_PART(full_name, ' ', 1) AS first_name,
    SPLIT_PART(full_name, ' ', 2) AS last_name,
    SPLIT_PART(full_name, ' ', 3) AS patronymic
FROM users;

7. Домен email

-- Postgres
SELECT email, SPLIT_PART(email, '@', 2) AS domain FROM users;

-- MySQL
SELECT email, SUBSTRING_INDEX(email, '@', -1) AS domain FROM users;

8. UTM из URL

-- Postgres (regex)
SELECT
    url,
    REGEXP_MATCHES(url, 'utm_source=([^&]+)') AS utm_source,
    REGEXP_MATCHES(url, 'utm_medium=([^&]+)') AS utm_medium
FROM visits;

9. Разбить CSV-колонку на строки

Если в колонке tags = 'vip,new,premium' — развернуть:

-- Postgres
SELECT user_id, unnest(STRING_TO_ARRAY(tags, ',')) AS tag
FROM users;

Это как реляционная нормализация на лету.

10. Обработка null

-- если null → пустая строка, чтобы SPLIT_PART не упал
SELECT SPLIT_PART(COALESCE(email, ''), '@', 2);

Частые ошибки

1. Индекс с 1 или 0

В SQL обычно с 1. В Python — с 0. Не путать.

2. Разделитель — regex vs literal

В REGEXP_SPLIT_TO_ARRAY — regex. В STRING_TO_ARRAY — литерал.

3. Пустые части

'a,,b' разбитое по ,['a', '', 'b']. 3 части, средняя пустая.

4. Performance на миллиардах

Разбиение в runtime медленное. Лучше нормализовать данные в ETL.

Связанные темы

FAQ

SPLIT_PART в MySQL?

Нет. Используйте SUBSTRING_INDEX.

Как разбить по нескольким разделителям?

Regex: REGEXP_SPLIT_TO_ARRAY('a,b;c', '[,;]').

Очень длинные CSV в колонке?

Рассмотрите нормализацию: отдельная таблица user_tags.

JSON как разбить?

Не через string split. Используйте JSON-функции.


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.