Learning Platform
Глоссарий Troubleshooting
Урок 09.02 · 25 мин
Продвинутый
ARRAY JOINLEFT ARRAY JOINarrayJoinunnestingArray

ARRAY JOIN и arrayJoin()

Массивы — базовый тип данных в ClickHouse. Таблицы часто содержат столбцы типа Array(String), Array(UInt32), Array(Tuple(String, UInt32)). Но анализировать данные удобнее в строковом формате: одна строка — один элемент. Для этого существует ARRAY JOIN.

ARRAY JOIN разворачивает массив в отдельные строки: одна строка с массивом из 3 элементов превращается в 3 строки. Это аналог UNNEST в PostgreSQL и LATERAL VIEW explode() в Spark.


Как работает ARRAY JOIN

ARRAY JOIN: разворачивание массива в строки
user_id=1, tags=[news, sport, tech]Исходная строка: user_id=1, tags=['news','sport','tech']. Один элемент таблицы содержит массив из 3 значений. Скалярные столбцы (user_id) дублируются для каждого элемента массива.
ARRAY JOIN
user_id=1, tag=newsПервая развёрнутая строка: user_id=1, tag='news'. Скалярный столбец user_id дублирован из исходной строки.
user_id=1, tag=sportВторая развёрнутая строка: user_id=1, tag='sport'. Каждый элемент массива становится отдельной строкой.
user_id=1, tag=techТретья развёрнутая строка: user_id=1, tag='tech'. Итого 3 строки из одной исходной -- по числу элементов массива.
SELECT user_id, tag
FROM events
ARRAY JOIN tags AS tag;
user_idtag
1news
1sport
1tech

Скалярные столбцы (user_id) дублируются для каждого элемента массива. Столбец-массив (tags) разворачивается в скалярный столбец (tag).


LEFT ARRAY JOIN: сохранение строк с пустыми массивами

Обычный ARRAY JOIN отбрасывает строки, где массив пуст ([]). Если пользователь не имеет тегов — он исчезнет из результата.

-- Строки с пустым tags[] исчезают:
SELECT user_id, tag
FROM events
ARRAY JOIN tags AS tag;
-- user_id=2, tags=[] -- НЕТ в результате

-- LEFT ARRAY JOIN сохраняет строки с пустыми массивами:
SELECT user_id, tag
FROM events
LEFT ARRAY JOIN tags AS tag;
-- user_id=2, tag='' -- пустая строка (default значение типа)

LEFT ARRAY JOIN аналогичен LEFT JOIN: строки без совпадений (пустой массив) сохраняются с default-значениями типа элемента массива ('' для String, 0 для UInt).


arrayJoin() — inline-разворачивание

Функция arrayJoin() разворачивает массив прямо в SELECT, без отдельного ARRAY JOIN clause:

-- Эквивалентно ARRAY JOIN tags AS tag:
SELECT user_id, arrayJoin(tags) AS tag
FROM events;
WARNING

arrayJoin() и ARRAY JOIN — не полностью идентичны. arrayJoin() применяется на уровне выражения в SELECT, а ARRAY JOIN — на уровне FROM. При использовании нескольких arrayJoin() в одном SELECT ClickHouse создаёт декартово произведение. С ARRAY JOIN clause можно развернуть несколько массивов параллельно.

-- arrayJoin() с выражением:
SELECT
    user_id,
    arrayJoin(arrayMap(x -> x * 2, scores)) AS doubled_score
FROM events;

Параллельное разворачивание нескольких массивов

ARRAY JOIN может разворачивать несколько массивов одновременно. Массивы должны иметь одинаковую длину в каждой строке:

-- tags = ['news', 'sport'], scores = [10, 20]
SELECT user_id, tag, score
FROM events
ARRAY JOIN
    tags AS tag,
    scores AS score;
user_idtagscore
1news10
1sport20

Элементы объединяются по позиции: первый элемент tags с первым элементом scores.

DANGER

Если массивы имеют разную длину, ClickHouse дополнит короткий массив default-значениями. Это часто приводит к неожиданным результатам. Всегда проверяйте длины массивов:

SELECT user_id, length(tags), length(scores)
FROM events
WHERE length(tags) != length(scores);

Практические примеры

Анализ тегов

-- Топ-10 самых популярных тегов
SELECT tag, count() AS cnt
FROM articles
ARRAY JOIN tags AS tag
GROUP BY tag
ORDER BY cnt DESC
LIMIT 10;

Разворачивание свойств событий

-- Nested-подобная структура: ключи и значения в парных массивах
-- prop_keys = ['device', 'browser', 'os']
-- prop_values = ['mobile', 'chrome', 'android']
SELECT
    event_id,
    key,
    value
FROM events
ARRAY JOIN
    prop_keys AS key,
    prop_values AS value;

Фильтрация по элементам массива

-- Все пользователи с тегом 'premium'
SELECT DISTINCT user_id
FROM events
ARRAY JOIN tags AS tag
WHERE tag = 'premium';
TIP

Для простой проверки наличия элемента в массиве используйте has(tags, ‘premium’) — это быстрее, чем ARRAY JOIN с фильтрацией. ARRAY JOIN нужен, когда необходимо работать с каждым элементом отдельно (агрегация, JOIN с другой таблицей).


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

ARRAY JOIN создаёт новые строки — это умножение данных. Массив из 100 элементов в 1 миллионе строк — 100 миллионов строк после ARRAY JOIN.

-- Проверка размера массивов перед ARRAY JOIN:
SELECT
    avg(length(tags)) AS avg_len,
    max(length(tags)) AS max_len,
    quantile(0.99)(length(tags)) AS p99_len
FROM events;

Если P99 длины массива больше 1000 — рассмотрите фильтрацию до ARRAY JOIN или предварительную агрегацию. Массивы с тысячами элементов могут привести к значительному потреблению памяти.


Ключевые выводы

  1. ARRAY JOIN разворачивает массив в строки: 1 строка с N элементами = N строк.
  2. LEFT ARRAY JOIN сохраняет строки с пустыми массивами (default-значения типа).
  3. arrayJoin() — inline-версия в SELECT, но создаёт декартово произведение при нескольких вызовах.
  4. Парные массивы разворачиваются параллельно по позиции; длины должны совпадать.
  5. Производительность: ARRAY JOIN умножает количество строк — проверяйте длины массивов перед использованием.
PostgreSQL: UNNEST и LATERAL для разворачивания массивов Spark: GROUP BY, aggregations и explode для массивов

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 3. Таблица events содержит столбец tags Array(String). У некоторых событий tags = [] (пустой массив). Нужно получить список всех событий с развёрнутыми тегами, включая события без тегов. Какой синтаксис корректен?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 10