26
0
0
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники
Назад

Объединения в Clickhouse, о которых вы не знали

Время чтения 10 минут
Нет времени читать?
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники
26
0
0
Нет времени читать?
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники

Приветствую специалистов по данным!

Я Павел Беляев, тимлид группы обработки данных в дата-офисе сервиса автоматизации интернет-рекламы eLama и автор канала «Тимлидское об аналитике». Мы разрабатываем и поддерживаем витрины данных в Clickhouse на языке SQL.

Большинство аналитиков в запросах с объединениями использует обычно ограниченный набор их типов: UNION, INNER JOIN, LEFT JOIN, на худой конец FULL OUTER JOIN. Вроде как а зачем что-то еще мудрить, раз этого хватает?

В этой статье я покажу, зачем и как сделать ваши запросы изящнее и эффективнее, а также как открыть новые возможности за счет применения «непопулярных» видов соединения таблиц.

Объединения в Clickhouse, о которых вы не знали

EXCEPT — отсекающий UNION

Оператор EXCEPT ставится между двумя «вертикально» объединяемыми таблицами и выводит в результат только те строки первой из них, которых нет в правой.

Например, такой код


CREATE TABLE t1 (a Integer, b varchar(255));
 INSERT INTO t1 (a, b) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (6, 'f');
CREATE TABLE t2 (a Integer, b varchar(255));
 INSERT INTO t2 (a, b) VALUES (1, 'a'), (2, 'b'), (4, 'c'), (5, 'd');
SELECT * FROM t1
EXCEPT
SELECT * FROM t2;

 

выведет результат:

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

INTERSECT — пересечение таблиц

В противовес EXCEPT оператор INTERSECT выводит в результат только строки, совпадающие в обеих таблицах. Для таблиц из предыдущего примера запрос


SELECT * FROM t1
 INTERSECT
 SELECT * FROM t2;

 

выведет

Попробуйте использовать INTERSECT для выявления наличия некоторой сущности в разных таблицах. Например, так можно найти пользователей, которые совершили покупку и оставили отзыв, если данные о покупках и отзывах находятся в разных таблицах.

ARRAY JOIN — «уплощаем» массивы

ARRAY JOIN — оператор, который позволяет «развернуть» массивы в таблице, выводя каждый элемент массива в отдельную строку. Это полезно, если данные хранятся в виде массивов, а для анализа их нужно представить в виде отдельных записей. ARRAY JOIN создает новые строки, дублируя значения из других столбцов для каждого элемента массива.

Поясню на примере. Положим, есть табличка t со значениями типа массив в столбце arr:

Тогда запрос


SELECT x, arr
 FROM t
 ARRAY JOIN arr;

 

«развернет» столбец с массивом в плоскую таблицу, где на одну ячейку приходится одно значение:

Замечу, что строка с пустым массивом [] не попала в результат. Чтобы выбрать и ее, нужно использовать LEFT ARRAY JOIN.

RIGHT JOIN — для оптимизации запросов

RIGHT JOIN применяется гораздо реже, чем LEFT JOIN хотя бы потому, что мы привыкли читать слева направо и сверху вниз. Но он реально помогает оптимизировать использование памяти и ускорить исполнение запроса в Clickhouse.

Известно, что эта СУБД вторую таблицу при операции джойна помещает в память. Но что делать, если мы хотим приджойнить тяжелую таблицу к более легкой? В этом-то нам и поможет RIGHT JOIN! За примером использования отсылаю читателя к моей предыдущей статье об оптимизации запросов в Clickhouse.

LEFT ANTI JOIN — отсекаем по совпавшим ключам

Оператор LEFT ANTI JOIN или ANTI LEFT JOIN возвращает строки из левой таблицы, для которых нет совпадений в правой таблице. Аналогичного эффекта можно достичь с помощью строки

LEFT JOIN ... WHERE right_table.column IS NULL

Но LEFT ANTI JOIN изящнее и эффективнее по части использования ресурсов СУБД.

Положим, у нас есть две таблицы:

  • all_users — все пользователи системы;
  • active_users — пользователи, совершившие действия за последний месяц.

Мы хотим найти пользователей, которые неактивны (нет записей в active_users). Запрос выглядит так:


SELECT au.user_id,
 au.name AS user_name
 FROM all_users au
 LEFT ANTI JOIN active_users ac
 ON au.user_id = ac.user_id

ASOF JOIN — объединение по нечеткому условию

Во многих СУБД реализована возможность джойнить по неравным ключам, то есть


… ON t1.id = t2.id AND
         t1.a > t2.a_begin AND t1.a <= t2.a_end

 

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

Допустим, у вас есть данные из системы веб-аналитики:

Таблица hits с хитами и событиями:

Таблица sessions с сессиями и источниками трафика:

Вы хотите найти последний непрямой источник сессии перед событием регистрации (event = ‘registration’). Хиты и сессии связаны идентификатором клиента, но мы можем соединить их по временной метке, взяв ближайшую к этому событию сессию. (Кстати, о подобном механизме определения источника события, только для BigQuery, я, было дело, рассказывал.) Тут-то мы и применим наш ASOF LEFT JOIN:


SELECT h.*, s.utm_channel
 FROM
 (
 SELECT *
 FROM hits
 WHERE event = 'registration'
 ) AS h
 
ASOF LEFT JOIN
 (
 SELECT *
 FROM sessions
 WHERE utm_channel != '(direct) / (none)' -- непрямые источники
 ) AS s ON s.client_id = h.client_id -- минимум одно условие равенства
 AND s.session_start <= h.event_timestamp -- только одно условие неравенства

 

Если всё сделали правильно, на выходе получим последний непрямой источник регистрации:

Резюме

Итак, мы рассмотрели несколько редко используемых видов объединения таблиц.

«Вертикальное» объединение:

  • EXCEPT оставляет только те строки первой таблицы, которые не нашлись во второй.
  • INTERSECT, наоборот, выводит лишь те строки первой, которые есть во второй.

«Горизонтальное» объединение:

  • ARRAY JOIN разделяет столбец с массивом по одному элементу на строку.
  • RIGHT JOIN — известный, но редко используемый — помогает снизить расход памяти Clickhouse за счет размещения более тяжелой таблицы первой.
  • LEFT ANTI JOIN — изящный аналог конструкции LEFT JOIN … WHERE t.column IS NULL.

ASOF JOIN позволяет присоединять таблицу по нечеткому условию, используется в основном при работе с временными рядами.

Комментарии0
Тоже интересно
Комментировать
Поделиться
Скопировать ссылку
Telegram
WhatsApp
Vkontakte
Одноклассники