mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-04 13:32:13 +00:00
188 lines
16 KiB
Markdown
188 lines
16 KiB
Markdown
# Секция JOIN {#select-join}
|
||
|
||
Join создаёт новую таблицу путем объединения столбцов из одной или нескольких таблиц с использованием общих для каждой из них значений. Это обычная операция в базах данных с поддержкой SQL, которая соответствует join из [реляционной алгебры](https://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators). Частный случай соединения одной таблицы часто называют «self-join».
|
||
|
||
Синтаксис:
|
||
|
||
``` sql
|
||
SELECT <expr_list>
|
||
FROM <left_table>
|
||
[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ASOF] JOIN <right_table>
|
||
(ON <expr_list>)|(USING <column_list>) ...
|
||
```
|
||
|
||
Выражения из секции `ON` и столбцы из секции `USING` называется «ключами соединения». Если не указано иное, при присоединение создаётся [Декартово произведение](https://en.wikipedia.org/wiki/Cartesian_product) из строк с совпадающими значениями ключей соединения, что может привести к получению результатов с гораздо большим количеством строк, чем исходные таблицы.
|
||
|
||
## Поддерживаемые типы соединения {#select-join-types}
|
||
|
||
Все типы из стандартого [SQL JOIN](https://en.wikipedia.org/wiki/Join_(SQL)) поддерживаются:
|
||
|
||
- `INNER JOIN`, возвращаются только совпадающие строки.
|
||
- `LEFT OUTER JOIN`, не совпадающие строки из левой таблицы возвращаются в дополнение к совпадающим строкам.
|
||
- `RIGHT OUTER JOIN`, не совпадающие строки из правой таблицы возвращаются в дополнение к совпадающим строкам.
|
||
- `FULL OUTER JOIN`, не совпадающие строки из обеих таблиц возвращаются в дополнение к совпадающим строкам.
|
||
- `CROSS JOIN`, производит декартово произведение таблиц целиком, ключи соединения не указываются.
|
||
|
||
Без указания типа `JOIN` подразумевается `INNER`. Ключевое слово `OUTER` можно опускать. Альтернативным синтаксисом для `CROSS JOIN` является ли указание нескольких таблиц, разделённых запятыми, в [секции FROM](from.md).
|
||
|
||
Дополнительные типы соединений, доступные в ClickHouse:
|
||
|
||
- `LEFT SEMI JOIN` и `RIGHT SEMI JOIN`, белый список по ключам соединения, не производит декартово произведение.
|
||
- `LEFT ANTI JOIN` и `RIGHT ANTI JOIN`, черный список по ключам соединения, не производит декартово произведение.
|
||
- `LEFT ANY JOIN`, `RIGHT ANY JOIN` и `INNER ANY JOIN`, Частично (для противоположных сторон `LEFT` и `RIGHT`) или полностью (для `INNER` и `FULL`) отключает декартово произведение для стандартых видов `JOIN`.
|
||
- `ASOF JOIN` и `LEFT ASOF JOIN`, Для соединения последовательностей по нечеткому совпадению. Использование `ASOF JOIN` описано ниже.
|
||
|
||
## Настройки {#join-settings}
|
||
|
||
!!! note "Примечание"
|
||
Значение строгости по умолчанию может быть переопределено с помощью настройки [join\_default\_strictness](../../../operations/settings/settings.md#settings-join_default_strictness).
|
||
|
||
### Использование ASOF JOIN {#asof-join-usage}
|
||
|
||
`ASOF JOIN` применим в том случае, когда необходимо объединять записи, которые не имеют точного совпадения.
|
||
|
||
Для работы алгоритма необходим специальный столбец в таблицах. Этот столбец:
|
||
|
||
- Должен содержать упорядоченную последовательность.
|
||
- Может быть одного из следующих типов: [Int*, UInt*](../../data-types/int-uint.md), [Float*](../../data-types/float.md), [Date](../../data-types/date.md), [DateTime](../../data-types/datetime.md), [Decimal*](../../data-types/decimal.md).
|
||
- Не может быть единственным столбцом в секции `JOIN`.
|
||
|
||
Синтаксис `ASOF JOIN ... ON`:
|
||
|
||
``` sql
|
||
SELECT expressions_list
|
||
FROM table_1
|
||
ASOF LEFT JOIN table_2
|
||
ON equi_cond AND closest_match_cond
|
||
```
|
||
|
||
Можно использовать произвольное количество условий равенства и одно условие на ближайшее совпадение. Например, `SELECT count() FROM table_1 ASOF LEFT JOIN table_2 ON table_1.a == table_2.b AND table_2.t <= table_1.t`.
|
||
|
||
Условия, поддержанные для проверки на ближайшее совпадение: `>`, `>=`, `<`, `<=`.
|
||
|
||
Синтаксис `ASOF JOIN ... USING`:
|
||
|
||
``` sql
|
||
SELECT expressions_list
|
||
FROM table_1
|
||
ASOF JOIN table_2
|
||
USING (equi_column1, ... equi_columnN, asof_column)
|
||
```
|
||
|
||
Для слияния по равенству `ASOF JOIN` использует `equi_columnX`, а для слияния по ближайшему совпадению использует `asof_column` с условием `table_1.asof_column >= table_2.asof_column`. Столбец `asof_column` должен быть последним в секции `USING`.
|
||
|
||
Например, рассмотрим следующие таблицы:
|
||
|
||
table_1 table_2
|
||
event | ev_time | user_id event | ev_time | user_id
|
||
----------|---------|---------- ----------|---------|----------
|
||
... ...
|
||
event_1_1 | 12:00 | 42 event_2_1 | 11:59 | 42
|
||
... event_2_2 | 12:30 | 42
|
||
event_1_2 | 13:00 | 42 event_2_3 | 13:00 | 42
|
||
... ...
|
||
|
||
`ASOF JOIN` принимает метку времени пользовательского события из `table_1` и находит такое событие в `table_2` метка времени которого наиболее близка к метке времени события из `table_1` в соответствии с условием на ближайшее совпадение. При этом столбец `user_id` используется для объединения по равенству, а столбец `ev_time` для объединения по ближайшему совпадению. В нашем примере `event_1_1` может быть объединено с `event_2_1`, `event_1_2` может быть объединено с `event_2_3`, а `event_2_2` не объединяется.
|
||
|
||
!!! note "Примечание"
|
||
`ASOF JOIN` не поддержан для движка таблиц [Join](../../../engines/table-engines/special/join.md).
|
||
|
||
Чтобы задать значение строгости по умолчанию, используйте сессионный параметр [join\_default\_strictness](../../../operations/settings/settings.md#settings-join_default_strictness).
|
||
|
||
#### Распределённый join {#global-join}
|
||
|
||
Есть два пути для выполнения соединения с участием распределённых таблиц:
|
||
|
||
- При использовании обычного `JOIN` , запрос отправляется на удалённые серверы. На каждом из них выполняются подзапросы для формирования «правой» таблицы, и с этой таблицей выполняется соединение. То есть, «правая» таблица формируется на каждом сервере отдельно.
|
||
- При использовании `GLOBAL ... JOIN`, сначала сервер-инициатор запроса запускает подзапрос для вычисления правой таблицы. Эта временная таблица передаётся на каждый удалённый сервер, и на них выполняются запросы с использованием переданных временных данных.
|
||
|
||
Будьте аккуратны при использовании `GLOBAL`. За дополнительной информацией обращайтесь в раздел [Распределенные подзапросы](#select-distributed-subqueries).
|
||
|
||
## Рекомендации по использованию {#usage-recommendations}
|
||
|
||
### Обработка пустых ячеек и NULL {#processing-of-empty-or-null-cells}
|
||
|
||
При соединении таблиц могут появляться пустые ячейки. Настройка [join\_use\_nulls](../../../operations/settings/settings.md#join_use_nulls) определяет, как ClickHouse заполняет эти ячейки.
|
||
|
||
Если ключами `JOIN` выступают поля типа [Nullable](../../../sql-reference/data-types/nullable.md), то строки, где хотя бы один из ключей имеет значение [NULL](../../../sql-reference/syntax.md#null-literal), не соединяются.
|
||
|
||
### Синтаксис {#syntax}
|
||
|
||
Требуется, чтобы столбцы, указанные в `USING`, назывались одинаково в обоих подзапросах, а остальные столбцы - по-разному. Изменить имена столбцов в подзапросах можно с помощью синонимов.
|
||
|
||
В секции `USING` указывается один или несколько столбцов для соединения, что обозначает условие на равенство этих столбцов. Список столбцов задаётся без скобок. Более сложные условия соединения не поддерживаются.
|
||
|
||
### Ограничения cинтаксиса {#syntax-limitations}
|
||
|
||
Для множественных секций `JOIN` в одном запросе `SELECT`:
|
||
|
||
- Получение всех столбцов через `*` возможно только при объединении таблиц, но не подзапросов.
|
||
- Секция `PREWHERE` недоступна.
|
||
|
||
Для секций `ON`, `WHERE` и `GROUP BY`:
|
||
|
||
- Нельзя использовать произвольные выражения в секциях `ON`, `WHERE`, и `GROUP BY`, однако можно определить выражение в секции `SELECT` и затем использовать его через алиас в других секциях.
|
||
|
||
### Производительность {#performance}
|
||
|
||
При запуске `JOIN`, отсутствует оптимизация порядка выполнения по отношению к другим стадиям запроса. Соединение (поиск в «правой» таблице) выполняется до фильтрации в `WHERE` и до агрегации. Чтобы явно задать порядок вычислений, рекомендуется выполнять `JOIN` подзапроса с подзапросом.
|
||
|
||
Каждый раз для выполнения запроса с одинаковым `JOIN`, подзапрос выполняется заново — результат не кэшируется. Это можно избежать, используя специальный движок таблиц [Join](../../../engines/table-engines/special/join.md), представляющий собой подготовленное множество для соединения, которое всегда находится в оперативке.
|
||
|
||
В некоторых случаях это более эффективно использовать [IN](../../operators/in.md) вместо `JOIN`.
|
||
|
||
Если `JOIN` необходим для соединения с таблицами измерений (dimension tables - сравнительно небольшие таблицы, которые содержат свойства измерений - например, имена для рекламных кампаний), то использование `JOIN` может быть не очень удобным из-за громоздкости синтаксиса, а также из-за того, что правая таблица читается заново при каждом запросе. Специально для таких случаев существует функциональность «Внешние словари», которую следует использовать вместо `JOIN`. Дополнительные сведения смотрите в разделе «Внешние словари».
|
||
|
||
|
||
### Ограничения по памяти {#memory-limitations}
|
||
|
||
По умолчанию ClickHouse использует алгоритм [hash join](https://en.wikipedia.org/wiki/Hash_join). ClickHouse берет `<right_table>` и создает для него хэш-таблицу в оперативной памяти. После некоторого порога потребления памяти ClickHouse переходит к алгоритму merge join.
|
||
|
||
- [max\_rows\_in\_join](../../../operations/settings/query-complexity.md#settings-max_rows_in_join) — ограничивает количество строк в хэш-таблице.
|
||
- [max\_bytes\_in\_join](../../../operations/settings/query-complexity.md#settings-max_bytes_in_join) — ограничивает размер хэш-таблицы.
|
||
|
||
По достижении любого из этих ограничений, ClickHouse действует в соответствии с настройкой [join\_overflow\_mode](../../../operations/settings/query-complexity.md#settings-join_overflow_mode).
|
||
|
||
## Примеры {#examples}
|
||
|
||
Пример:
|
||
|
||
``` sql
|
||
SELECT
|
||
CounterID,
|
||
hits,
|
||
visits
|
||
FROM
|
||
(
|
||
SELECT
|
||
CounterID,
|
||
count() AS hits
|
||
FROM test.hits
|
||
GROUP BY CounterID
|
||
) ANY LEFT JOIN
|
||
(
|
||
SELECT
|
||
CounterID,
|
||
sum(Sign) AS visits
|
||
FROM test.visits
|
||
GROUP BY CounterID
|
||
) USING CounterID
|
||
ORDER BY hits DESC
|
||
LIMIT 10
|
||
```
|
||
|
||
``` text
|
||
┌─CounterID─┬───hits─┬─visits─┐
|
||
│ 1143050 │ 523264 │ 13665 │
|
||
│ 731962 │ 475698 │ 102716 │
|
||
│ 722545 │ 337212 │ 108187 │
|
||
│ 722889 │ 252197 │ 10547 │
|
||
│ 2237260 │ 196036 │ 9522 │
|
||
│ 23057320 │ 147211 │ 7689 │
|
||
│ 722818 │ 90109 │ 17847 │
|
||
│ 48221 │ 85379 │ 4652 │
|
||
│ 19762435 │ 77807 │ 7026 │
|
||
│ 722884 │ 77492 │ 11056 │
|
||
└───────────┴────────┴────────┘
|
||
```
|