NULL in requests.

This commit is contained in:
BayoNet 2018-07-17 08:48:14 +03:00
parent ae8c739c69
commit 3a6b95870c
5 changed files with 197 additions and 26 deletions

View File

@ -14,6 +14,7 @@ extra_css:
markdown_extensions:
- codehilite
- attr_list
theme:
name: null

View File

@ -10,9 +10,9 @@ ClickHouse применяет настройку в том случае, ког
Условия применения:
- Только подзапросы для IN, JOIN.
- Только если в секции FROM используется распределённая таблица.
- Не используется в случае табличной функции [remote](../../table_functions/remote.md#table_functions-remote).
- Только подзапросы для IN, JOIN.
- Только если в секции FROM используется распределённая таблица.
- Не используется в случае табличной функции [remote](../../table_functions/remote.md#table_functions-remote).
Возможные значения:
@ -341,3 +341,11 @@ ClickHouse применяет настройку в том случае, ког
## format_csv_delimiter
Символ, интерпретируемый как разделитель в данных формата CSV. По умолчанию — `,`.
<!--a name="settings-join_use_nulls"></a-->
## join_use_nulls {: #settings-join_use_nulls}
Влияет на поведение [JOIN](../../query_language/queries.md#query_language-join).
При `join_use_nulls=1` `JOIN` ведёт себя как в стандартном SQL, т.е. если при слиянии возникают пустые ячейки, то тип соответствующего поля преобразуется к [Nullable](../../data_types/nullable.md#data_type-nullable), а пустые ячейки заполняются значениями [NULL](../../query_language/syntax.md#null-literal).

View File

@ -124,6 +124,8 @@ END
ClickHouse поддерживает операторы `IS NULL` и `IS NOT NULL`.
<a name="operator-is-null"></a>
### IS NULL
- Для значений типа [Nullable](../data_types/nullable.md#data_type-nullable) оператор `IS NULL` возвращает:
@ -145,6 +147,8 @@ WHERE isNull(y)
1 rows in set. Elapsed: 0.002 sec.
```
<a name="operator-is-not-null"></a>
### IS NOT NULL
- Для значений типа [Nullable](../data_types/nullable.md#data_type-nullable) оператор `IS NOT NULL` возвращает:

View File

@ -1003,6 +1003,9 @@ ARRAY JOIN nest AS n, arrayEnumerate(`nest.x`) AS num
Соответствующее преобразование может выполняться как до секции WHERE/PREWHERE (если его результат нужен в этой секции), так и после выполнения WHERE/PREWHERE (чтобы уменьшить объём вычислений).
<a name="query_language-join"></a>
### Секция JOIN
Обычный JOIN, не имеет отношения к ARRAY JOIN, который описан выше.
@ -1099,29 +1102,69 @@ LIMIT 10
Если JOIN необходим для соединения с таблицами измерений (dimension tables - сравнительно небольшие таблицы, которые содержат свойства измерений - например, имена для рекламных кампаний), то использование JOIN может быть не очень удобным из-за громоздкости синтаксиса, а также из-за того, что правая таблица читается заново при каждом запросе. Специально для таких случаев существует функциональность "Внешние словари", которую следует использовать вместо JOIN. Подробнее смотрите раздел "Внешние словари".
#### Обработка NULL
На поведение JOIN влияет настройка [join_use_nulls](../operations/settings/settings.md#settings-join_use_nulls). При `join_use_nulls=1` `JOIN` работает как в стандартном SQL.
Если ключами JOIN выступают поля типа [Nullable](../data_types/nullable.md#data_types-nullable), то строки, где хотя бы один из ключей имеет значение [NULL](syntax.md#null-literal), не соединяются.
<a name="query_language-queries-where"></a>
### Секция WHERE
Секция WHERE, если есть, должна содержать выражение, имеющее тип UInt8. Обычно это какое-либо выражение с операторами сравнения и логическими операторами.
Это выражение будет использовано для фильтрации данных перед всеми остальными преобразованиями.
Позволяет задать выражение, которое ClickHouse использует для фильтрации данных перед всеми другими действиями в запросе кроме выражений, содержащихся в секции [PREWHERE](#query_language-queries-prewhere). Обычно, это выражение с логическими операторами.
Выражение анализируется на возможность использования индексов, если индексы поддерживаются движком таблицы.
Результат выражения должен иметь тип `UInt8`.
ClickHouse использует в выражении индексы, если это позволяет [движок таблицы](../table_engines/index.md#table_engines).
Если в секции необходимо проверить [NULL](syntax.md#null-literal), то используйте операторы [IS NULL](../operators/index.md#operator-is-null) и [IS NOT NULL](../operators/index.md#operator-is-not-null), а также соответствующие функции `isNull` и `isNotNull`. В противном случае выражение будет считаться всегда не выполненным.
Пример проверки на `NULL`:
```bash
:) SELECT * FROM t_null WHERE y=NULL
SELECT *
FROM t_null
WHERE y = NULL
Ok.
0 rows in set. Elapsed: 0.002 sec.
:) SELECT * FROM t_null WHERE y IS NULL
SELECT *
FROM t_null
WHERE isNull(y)
┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
└───┴──────┘
1 rows in set. Elapsed: 0.002 sec.
```
<a name="query_language-queries-prewhere"></a>
### Секция PREWHERE
Имеет такой же смысл, как и секция WHERE. Отличие состоит в том, какие данные читаются из таблицы.
При использовании PREWHERE, из таблицы сначала читаются только столбцы, необходимые для выполнения PREWHERE. Затем читаются остальные столбцы, нужные для выполнения запроса, но из них только те блоки, в которых выражение в PREWHERE истинное.
Имеет такой же смысл, как и секция [WHERE](#query_language-queries-where). Отличие состоит в том, какие данные читаются из таблицы.
При использовании `PREWHERE`, из таблицы сначала читаются только столбцы, необходимые для выполнения `PREWHERE`. Затем читаются остальные столбцы, нужные для выполнения запроса, но из них только те блоки, в которых выражение в `PREWHERE` истинное.
PREWHERE имеет смысл использовать, если есть условия фильтрации, не подходящие под индексы, которые использует меньшинство столбцов из тех, что есть в запросе, но достаточно сильно фильтрует данные. Таким образом, сокращается количество читаемых данных.
`PREWHERE` имеет смысл использовать, если есть условия фильтрации, не подходящие под индексы, которые использует меньшинство столбцов из тех, что есть в запросе, но достаточно сильно фильтрует данные. Таким образом, сокращается количество читаемых данных.
Например, полезно писать PREWHERE для запросов, которые вынимают много столбцов, но в которых фильтрация производится лишь по нескольким столбцам.
Например, полезно писать `PREWHERE` для запросов, которые вынимают много столбцов, но в которых фильтрация производится лишь по нескольким столбцам.
PREWHERE поддерживается только таблицами семейства `*MergeTree`.
`PREWHERE` поддерживается только таблицами семейства `*MergeTree`.
В запросе могут быть одновременно указаны секции PREWHERE и WHERE. В этом случае, PREWHERE идёт перед WHERE.
В запросе могут быть одновременно указаны секции `PREWHERE` и `WHERE`. В этом случае, `PREWHERE` идёт перед `WHERE`.
Следует иметь ввиду, что указывать в PREWHERE только столбцы, по которым существует индекс, имеет мало смысла, так как при использовании индекса и так читаются лишь блоки данных, соответствующие индексу.
Следует иметь ввиду, что указывать в `PREWHERE` только столбцы, по которым существует индекс, имеет мало смысла, так как при использовании индекса и так читаются лишь блоки данных, соответствующие индексу.
Если настройка optimize_move_to_prewhere выставлена в 1, то при отсутствии PREWHERE, система будет автоматически переносить части выражений из WHERE в PREWHERE согласно некоторой эвристике.
Если настройка `optimize_move_to_prewhere` выставлена в `1`, то при отсутствии `PREWHERE`, система будет автоматически переносить части выражений из `WHERE` в `PREWHERE` согласно некоторой эвристике.
### Секция GROUP BY
@ -1163,6 +1206,38 @@ GROUP BY вычисляет для каждого встретившегося
Не поддерживается указание констант в качестве аргументов агрегатных функций. Пример: sum(1). Вместо этого, вы можете избавиться от констант. Пример: `count()`.
#### Обработка NULL
При группировке, ClickHouse рассматривает [NULL](syntax.md#null-literal) как значение, причём `NULL=NULL`.
Рассмотрим, что это значит на примере.
Пусть есть таблица:
```
┌─x─┬────y─┐
│ 1 │ 2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │ 2 │
│ 3 │ 3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘
```
В результате запроса `SELECT sum(x),y FROM t_null_big GROUP BY y` мы получим:
```
┌─sum(x)─┬────y─┐
│ 4 │ 2 │
│ 3 │ 3 │
│ 5 │ ᴺᵁᴸᴸ │
└────────┴──────┘
```
Видно, что `GROUP BY` для `У = NULL` просуммировал `x`, как будто `NULL` — это значение.
Если в `GROUP BY` передать несколько ключей, то в результате мы получим все комбинации выборки, как если бы `NULL` был конкретным значением.
#### Модификатор WITH TOTALS
Если указан модификатор WITH TOTALS, то будет посчитана ещё одна строчка, в которой в столбцах-ключах будут содержаться значения по умолчанию (нули, пустые строки), а в столбцах агрегатных функций - значения, посчитанные по всем строкам ("тотальные" значения).
@ -1208,7 +1283,7 @@ GROUP BY вычисляет для каждого встретившегося
### Секция LIMIT N BY
LIMIT N BY COLUMNS выбирает топ N строк для каждой группы COLUMNS. LIMIT N BY не связан с LIMIT и они могут использоваться в одном запросе. Ключ для LIMIT N BY может содержать произвольное число колонок или выражений.
`LIMIT N BY COLUMNS` выбирает топ `N` строк для каждой группы `COLUMNS`. `LIMIT N BY` не связан с `LIMIT` и они могут использоваться в одном запросе. Ключ для `LIMIT N BY` может содержать произвольное число колонок или выражений.
Пример:
@ -1227,6 +1302,8 @@ LIMIT 100
Запрос выберет топ 5 рефереров для каждой пары `domain, device_type`, но не более 100 строк (`LIMIT n BY + LIMIT`).
`LIMIT n BY` работает с [NULL](syntax.md#null-literal) как если бы это было конкретное значение. Т.е. в результате запроса пользователь получит все комбинации полей, указанных в `BY`.
### Секция HAVING
Позволяет отфильтровать результат, полученный после GROUP BY, аналогично секции WHERE.
@ -1246,7 +1323,47 @@ WHERE и HAVING отличаются тем, что WHERE выполняется
Строки, для которых список выражений, по которым производится сортировка, принимает одинаковые значения, выводятся в произвольном порядке, который может быть также недетерминированным (каждый раз разным).
Если секция ORDER BY отсутствует, то, аналогично, порядок, в котором идут строки, не определён, и может быть недетерминированным.
При сортировке чисел с плавающей запятой, NaN-ы идут отдельно от остальных значений. Вне зависимости от порядка сортировки, NaN-ы помещаются в конец. То есть, при сортировке по возрастанию, они как будто больше всех чисел, а при сортировке по убыванию - как будто меньше всех.
Порядок сортировки `NaN` и `NULL`:
- С модификатором `NULLS FIRST` — Сначала `NULL`, затем `NaN`, затем остальные значения.
- С модификатором `NULLS LAST` — Сначала значения, затем `NaN`, затем `NULL`.
- Без модификаторов `NULLS FIRST` или `NULLS LAST` как с модификатором `NULLS LAST`.
Пример:
Для таблицы
```
┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 2 │ 2 │
│ 1 │ nan │
│ 2 │ 2 │
│ 3 │ 4 │
│ 5 │ 6 │
│ 6 │ nan │
│ 7 │ ᴺᵁᴸᴸ │
│ 6 │ 7 │
│ 8 │ 9 │
└───┴──────┘
```
Выполним запрос `SELECT * FROM t_null_nan ORDER BY y NULLS FIRST`, получим:
```
┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 7 │ ᴺᵁᴸᴸ │
│ 1 │ nan │
│ 6 │ nan │
│ 2 │ 2 │
│ 2 │ 2 │
│ 3 │ 4 │
│ 5 │ 6 │
│ 6 │ 7 │
│ 8 │ 9 │
└───┴──────┘
```
Если кроме ORDER BY указан также не слишком большой LIMIT, то расходуется меньше оперативки. Иначе расходуется количество памяти, пропорциональное количеству данных для сортировки. При распределённой обработке запроса, если отсутствует GROUP BY, сортировка частично делается на удалённых серверах, а на сервере-инициаторе запроса производится слияние результатов. Таким образом, при распределённой сортировке, может сортироваться объём данных, превышающий размер памяти на одном сервере.
@ -1265,14 +1382,16 @@ WHERE и HAVING отличаются тем, что WHERE выполняется
### Секция DISTINCT
Если указано DISTINCT, то из всех множеств полностью совпадающих строк результата, будет оставляться только одна строка.
Результат выполнения будет таким же, как если указано GROUP BY по всем указанным полям в SELECT-е и не указаны агрегатные функции. Но имеется несколько отличий от GROUP BY:
Если указано `DISTINCT`, то из всех множеств полностью совпадающих строк результата, будет оставляться только одна строка.
Результат выполнения будет таким же, как если указано `GROUP BY` по всем указанным полям в `SELECT` и не указаны агрегатные функции. Но имеется несколько отличий от `GROUP BY`:
- DISTINCT может применяться совместно с GROUP BY;
- при отсутствии ORDER BY и наличии LIMIT, запрос прекратит выполнение сразу после того, как будет прочитано необходимое количество различных строк - в этом случае использование DISTINCT существенно более оптимально;
- `DISTINCT` может применяться совместно с `GROUP BY`;
- при отсутствии `ORDER BY` и наличии `LIMIT`, запрос прекратит выполнение сразу после того, как будет прочитано необходимое количество различных строк - в этом случае использование DISTINCT существенно более оптимально;
- блоки данных будут выдаваться по мере их обработки, не дожидаясь выполнения всего запроса.
DISTINCT не поддерживается, если в SELECT-е присутствует хотя бы один столбец типа массив.
`DISTINCT` не поддерживается, если в `SELECT` присутствует хотя бы один столбец типа массив.
`DISTINCT` работает с [NULL](syntax.md#null-literal) как если бы `NULL` был конкретным значением, причём `NULL=NULL`. Т.е. в результате `DISTINCT` разные комбинации с `NULL` встретятся только по одному разу.
### Секция LIMIT
@ -1283,9 +1402,11 @@ n и m должны быть неотрицательными целыми чи
При отсутствии секции ORDER BY, однозначно сортирующей результат, результат может быть произвольным и может являться недетерминированным.
`DISTINCT` работает с [NULL](syntax.md#null-literal) как если бы `NULL` был конкретным значением, причём `NULL=NULL`. Т.е. в результате `DISTINCT` разные комбинации с `NULL` встретятся только по одному разу.
### Секция UNION ALL
Произвольное количество запросов может быть объединено с помощью UNION ALL. Пример:
Произвольное количество запросов может быть объединено с помощью `UNION ALL`. Пример:
```sql
SELECT CounterID, 1 AS table, toInt64(count()) AS c
@ -1300,13 +1421,13 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c
HAVING c > 0
```
Поддерживается только UNION ALL. Обычный UNION (UNION DISTINCT) не поддерживается. Если вам нужен UNION DISTINCT, то вы можете написать SELECT DISTINCT из подзапроса, содержащего UNION ALL.
Поддерживается только `UNION ALL`. Обычный `UNION` (`UNION DISTINCT`) не поддерживается. Если вам нужен `UNION DISTINCT`, то вы можете написать `SELECT DISTINCT` из подзапроса, содержащего `UNION ALL`.
Запросы - части UNION ALL могут выполняться параллельно, и их результаты могут возвращаться вперемешку.
Запросы - части `UNION ALL` могут выполняться параллельно, и их результаты могут возвращаться вперемешку.
Структура результатов (количество и типы столбцов) у запросов должна совпадать. Но имена столбцов могут отличаться. В этом случае, имена столбцов для общего результата будут взяты из первого запроса.
Структура результатов (количество и типы столбцов) у запросов должна совпадать. Но имена столбцов могут отличаться. В этом случае, имена столбцов для общего результата будут взяты из первого запроса. При объединении выполняется приведение типов. Например, если в двух объединяемых запросах одно и тоже поле имеет типы не-`Nullable` и `Nullable` от совместимого типа, то в результате `UNION ALL` получим поле типа `Nullable`.
Запросы - части UNION ALL нельзя заключить в скобки. ORDER BY и LIMIT применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с UNION ALL в подзапросе в секции FROM.
Запросы - части `UNION ALL` нельзя заключить в скобки. `ORDER BY` и `LIMIT` применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с `UNION ALL` в подзапросе в секции `FROM`.
### Секция INTO OUTFILE
@ -1344,6 +1465,7 @@ SELECT (CounterID, UserID) IN ((34, 123), (101500, 456)) FROM ...
В качестве правой части оператора может быть множество константных выражений, множество кортежей с константными выражениями (показано в примерах выше), а также имя таблицы или подзапрос SELECT в скобках.
Если в качестве правой части оператора указано имя таблицы (например, `UserID IN users`), то это эквивалентно подзапросу `UserID IN (SELECT * FROM users)`. Это используется при работе с внешними данными, отправляемым вместе с запросом. Например, вместе с запросом может быть отправлено множество идентификаторов посетителей, загруженное во временную таблицу users, по которому следует выполнить фильтрацию.
Если качестве правой части оператора, указано имя таблицы, имеющий движок Set (подготовленное множество, постоянно находящееся в оперативке), то множество не будет создаваться заново при каждом запросе.
@ -1389,6 +1511,40 @@ ORDER BY EventDate ASC
за каждый день после 17 марта считаем долю хитов, сделанных посетителями, которые заходили на сайт 17 марта.
Подзапрос в секции IN на одном сервере всегда выполняется только один раз. Зависимых подзапросов не существует.
#### Обработка NULL
При обработке запроса оператор IN будет считать, что результат операции с [NULL](syntax.md#null-literal) всегда равен `0`, независимо от того, находится `NULL` в правой или левой части оператора. Значения `NULL` не входят ни в какое множество, не соответствуют друг другу и не могут сравниваться.
Рассмотрим для примера таблицу `t_null`:
```
┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 2 │ 3 │
└───┴──────┘
```
При выполнении запроса `SELECT x FROM t_null WHERE y IN (NULL,3)` получим следующий результат:
```
┌─x─┐
│ 2 │
└───┘
```
Видно, что строка, в которой `y = NULL`, выброшена из результатов запроса. Это произошло потому, что ClickHouse не может решить входит ли `NULL` в множество `(NULL,3)`, возвращает результат операции `0`, а `SELECT` выбрасывает эту строку из финальной выдачи.
```
SELECT y IN (NULL, 3)
FROM t_null
┌─in(y, tuple(NULL, 3))─┐
│ 0 │
│ 1 │
└───────────────────────┘
```
<a name="queries-distributed-subrequests"></a>
#### Распределённые подзапросы

View File

@ -79,6 +79,8 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
При обработке `NULL` есть множество особенностей. Например, если хотя бы один из аргументов операции сравнения — `NULL`, то результатом такой операции тоже будет `NULL`. Этим же свойством обладают операции умножения, сложения и пр. Подробнее читайте в документации на каждую операцию.
В запросах можно проверить `NULL` с помощью операторов [IS NULL](../operators/index.md#operator-is-null) и [IS NOT NULL](../operators/index.md#operator-is-not-null), а также соответствующих функций `isNull` и `isNotNull`.
## Функции
Функции записываются как идентификатор со списком аргументов (возможно, пустым) в скобках. В отличие от стандартного SQL, даже в случае пустого списка аргументов, скобки обязательны. Пример: `now()`.