Как развернуть массив (UNNEST) в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
В современных event-таблицах часто встречаются массивы: tags = ['sql', 'python'], items_bought = [{id:1}, {id:2}]. Запрос к таким полям требует «развернуть» массив в строки — это и есть UNNEST.
В BigQuery и ClickHouse массивы — норма. В Postgres тоже часто встречаются. На собесах middle+ знание UNNEST — обязательно.
UNNEST в Postgres
SELECT unnest(ARRAY[1, 2, 3]) AS n;Вернёт 3 строки.
С таблицей:
SELECT user_id, tag
FROM users, unnest(tags) AS tag;users.tags — массив → развернёт.
UNNEST в BigQuery
SELECT user_id, tag
FROM users, UNNEST(tags) AS tag;Или с CROSS JOIN:
SELECT user_id, tag
FROM users
CROSS JOIN UNNEST(tags) AS tag;arrayJoin в ClickHouse
SELECT user_id, arrayJoin(tags) AS tag
FROM users;Аналог UNNEST.
С индексом
Если нужен индекс элемента:
Postgres:
SELECT user_id, tag, idx
FROM users, unnest(tags) WITH ORDINALITY AS t(tag, idx);BigQuery:
SELECT user_id, tag, idx
FROM users, UNNEST(tags) AS tag WITH OFFSET idx;Массив из массива
Двумерный array → unnest дважды:
SELECT inner_val
FROM t, unnest(outer_array) AS arr, unnest(arr) AS inner_val;JSON arrays
Postgres:
SELECT user_id, elem
FROM t, jsonb_array_elements(items) AS elem;BigQuery:
SELECT user_id, elem
FROM t, UNNEST(JSON_EXTRACT_ARRAY(items)) AS elem;Аггрегация после UNNEST
Top tags:
SELECT tag, COUNT(*) AS cnt
FROM users, unnest(tags) AS tag
GROUP BY tag
ORDER BY cnt DESC;Обратная операция: ARRAY_AGG
Свернуть обратно в массив:
SELECT user_id, array_agg(action ORDER BY ts) AS actions
FROM events
GROUP BY user_id;LATERAL для UNNEST
Когда массив зависит от row:
SELECT u.*, t.tag
FROM users u,
LATERAL unnest(u.tags) AS t(tag);Без LATERAL Postgres может ругаться на dependency.
Использование
Теги и categories
«Пользователи с тэгом sql»:
SELECT * FROM users
WHERE 'sql' = ANY(tags);Быстрее чем unnest + filter.
Event arrays
В event-based storage часто массив actions в одной строке → unnest для анализа.
JSON path
SELECT data->'items'->>0 FROM logs; -- первый элементНо для аналитики всех элементов — unnest.
Производительность
UNNEST на больших массивах — медленно, особенно без индексов. Если часто нужен анализ элементов — подумайте, правильная ли структура таблицы.
На собесе
«Таблица users с колонкой tags (array). Найди топ-10 тэгов по популярности».
SELECT tag, COUNT(*) AS cnt
FROM users, unnest(tags) AS tag
GROUP BY tag
ORDER BY cnt DESC
LIMIT 10;Связанные темы
FAQ
UNNEST в MySQL?
Нет нативного. Через JSON_TABLE в 8.0+.
Быстрее ли WHERE ... ANY(arr)?
Да, индексируется через GIN. UNNEST — для анализа всех элементов.
NULL в массиве?
unnest пропускает NULLs? Нет, вернёт как обычный row с NULL.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.