В этом разделе мы рассмотрим синтаксис SQL в ClickHouse. ClickHouse использует синтаксис на основе SQL, но предлагает ряд расширений и оптимизаций.
Разбор запросов
В ClickHouse есть два типа парсеров:
- Полный SQL-парсер (парсер рекурсивного спуска).
- Парсер формата данных (быстрый потоковый парсер).
Полный SQL-парсер используется во всех случаях, кроме запроса INSERT, в котором используются оба парсера.
Давайте рассмотрим приведённый ниже запрос:
INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')Как уже упоминалось, запрос INSERT использует оба парсера.
Фрагмент INSERT INTO t VALUES разбирается полным парсером,
а данные (1, 'Hello, world'), (2, 'abc'), (3, 'def') — парсером формата данных, или быстрым потоковым парсером.
Включение полного парсера
Вы также можете включить для данных полный парсер
с помощью настройки input_format_values_interpret_expressions.
Когда указанная выше настройка имеет значение 1,
ClickHouse сначала пытается разобрать значения быстрым потоковым парсером.
Если это не удаётся, ClickHouse пытается использовать для данных полный парсер, рассматривая их как SQL-выражение.
Данные могут быть в любом формате.
При получении запроса сервер держит в оперативной памяти не более max_query_size байт запроса
(по умолчанию 1 МБ), а остальная часть разбирается потоково.
Это позволяет избежать проблем с большими запросами INSERT, которые являются рекомендуемым способом вставки данных в ClickHouse.
При использовании формата Values в запросе INSERT
может показаться, что данные разбираются так же, как и выражения в запросе SELECT, однако это не так.
Формат Values значительно более ограничен.
Остальная часть этого раздела посвящена полному парсеру.
Пробелы
- Между синтаксическими конструкциями может находиться любое количество пробельных символов (в том числе в начале и в конце запроса).
- К пробельным символам относятся пробел, табуляция, перевод строки, CR и перевод страницы.
ClickHouse поддерживает комментарии как в стиле SQL, так и в стиле C:
- Комментарии в стиле SQL начинаются с
--,#!или#и продолжаются до конца строки. Пробел после--и#!можно опускать. - Комментарии в стиле C:
//(или более двух символов/), после которых идёт текст до конца строки. Пробелы после/не обязательны.- Для многострочных комментариев могут начинаться с
/*и заканчиваться*/. Пробелы также не обязательны. - Комментарии в стиле C могут быть вложенными.
Например:
/*
* Вычислить количество дней между двумя датами.
* /* Возвращает NULL, если любой из аргументов равен NULL */
*/
SELECT
dateDiff('day', toDate('2024-01-01'), toDate('2024-12-31')) AS days_in_year, -- 365
dateDiff('day', toDate('2020-01-01'), today()) AS days_since #! since 2020
///////////////////////////////////////////////////////////////////
# TODO: добавить варианты для часов/минутКлючевые слова
Ключевые слова в ClickHouse могут быть чувствительными к регистру или регистронезависимыми — в зависимости от контекста.
Ключевые слова регистронезависимы, если они относятся к:
- стандарту SQL. Например,
SELECT,selectиSeLeCt— все это допустимые варианты. - реализации в некоторых популярных СУБД (MySQL или Postgres). Например,
DateTime— это то же самое, что иdatetime.
В отличие от стандартного SQL, все остальные ключевые слова (включая имена функций) чувствительны к регистру.
Кроме того, ключевые слова не являются зарезервированными. Они считаются таковыми только в соответствующем контексте. Если вы используете идентификаторы с тем же именем, что и ключевые слова, заключайте их в двойные или обратные кавычки.
Например, следующий запрос допустим, если в таблице table_name есть столбец с именем "FROM":
SELECT "FROM" FROM table_nameИдентификаторы
Идентификаторы — это:
- Имена кластеров, баз данных, таблиц, партиций и столбцов.
- Функции.
- Типы данных.
- Псевдонимы выражений.
Идентификаторы могут быть заключены в кавычки или записаны без кавычек, хотя предпочтителен второй вариант.
Идентификаторы без кавычек должны соответствовать регулярному выражению ^[a-zA-Z_][0-9a-zA-Z_]*$ и не могут совпадать с ключевыми словами.
В таблице ниже приведены примеры допустимых и недопустимых идентификаторов:
| Допустимые идентификаторы | Недопустимые идентификаторы |
|---|---|
xyz, _internal, Id_with_underscores_123_ |
1x, tom@gmail.com, äußerst_schön |
Если вы хотите использовать идентификаторы, совпадающие с ключевыми словами, или использовать в идентификаторах другие символы, заключайте их в двойные кавычки или обратные кавычки, например "id", `id`.
Литералы
В ClickHouse литерал — это значение, которое напрямую указано в запросе. Иными словами, это фиксированное значение, которое не меняется в ходе выполнения запроса.
Литералы могут быть:
- Строковыми
- Числовыми
- Составными
NULL- Heredocs (пользовательские строковые литералы)
Ниже мы подробнее рассмотрим каждый из этих типов в соответствующих разделах.
String
Строковые литералы должны быть заключены в одинарные кавычки. Двойные кавычки не поддерживаются.
Экранирование выполняется одним из двух способов:
- с помощью предшествующей одинарной кавычки: символ одинарной кавычки
'(и только он) можно экранировать как'', или - с помощью предшествующей обратной косой черты с использованием поддерживаемых escape-последовательностей, перечисленных в таблице ниже.
| Поддерживаемое экранирование | Описание |
|---|---|
\xHH |
Обозначение 8-битного символа, за которым следует любое количество шестнадцатеричных цифр (H). |
\N |
зарезервировано, ничего не делает (например, SELECT 'a\Nb' возвращает ab) |
\a |
звуковой сигнал |
\b |
возврат на один символ |
\e |
escape-символ |
\f |
перевод страницы |
\n |
перевод строки |
\r |
возврат каретки |
\t |
горизонтальная табуляция |
\v |
вертикальная табуляция |
\0 |
нулевой символ |
\\ |
обратная косая черта |
\' (or '') |
одинарная кавычка |
\" |
двойная кавычка |
` |
обратная кавычка |
\/ |
прямая косая черта |
\= |
знак равенства |
| Управляющие символы ASCII (c <= 31). |
Числовые литералы
Числовые литералы разбираются следующим образом:
- Если литерал начинается со знака минус
-, токен пропускается, а после разбора к результату применяется отрицание. - Сначала числовой литерал разбирается как 64-битное беззнаковое целое число с помощью функции strtoull.
- Если значение начинается с
0bили0x/0X, число разбирается как двоичное или шестнадцатеричное соответственно. - Если значение отрицательное и его абсолютная величина больше 263, возвращается ошибка.
- Если значение начинается с
- Если это не удалось, значение затем разбирается как число с плавающей точкой с помощью функции strtod.
- В противном случае возвращается ошибка.
Значения литералов приводятся к наименьшему типу, в который помещается значение. Например:
1разбирается какUInt8256разбирается какUInt16.
Дополнительные сведения см. в разделе Типы данных.
Символы подчёркивания _ внутри числовых литералов игнорируются и могут использоваться для лучшей читаемости.
Поддерживаются следующие числовые литералы:
| Числовой литерал | Примеры |
|---|---|
| Целые числа | 1, 10_000_000, 18446744073709551615, 01 |
| Десятичные числа | 0.1 |
| Экспоненциальная запись | 1e100, -1e-100 |
| Числа с плавающей точкой | 123.456, inf, nan |
| Шестнадцатеричные | 0xc0fe |
| Шестнадцатеричная строка, совместимая со стандартом SQL | x'c0fe' |
| Двоичные | 0b1101 |
| Двоичная строка, совместимая со стандартом SQL | b'1101' |
Составные
Массивы создаются с помощью []: [1, 2, 3]. Кортежи создаются с помощью (): (1, 'Hello, world!', 2).
Технически это не литералы, а выражения с оператором создания массива и оператором создания кортежа соответственно.
Массив должен состоять как минимум из одного элемента, а кортеж — как минимум из двух.
NULL
NULL используется для обозначения отсутствующего значения.
Чтобы сохранить NULL в поле таблицы, оно должно иметь тип Nullable.
Heredoc
Heredoc — это способ задать строку (часто многострочную), сохранив исходное форматирование.
Heredoc представляет собой пользовательский строковый литерал, заключённый между двумя символами $.
Например:
SELECT $heredoc$SHOW CREATE VIEW my_view$heredoc$;
┌Определение и использование параметров запроса
Параметры запроса позволяют писать универсальные запросы, содержащие абстрактные плейсхолдеры вместо конкретных идентификаторов. Когда выполняется запрос с параметрами, все плейсхолдеры разрешаются и заменяются фактическими значениями параметров запроса.
Параметры запроса можно задавать несколькими способами:
SET param_<name>=<value>— с помощью командыSETв запросе.--param_<name>='<value>'— как аргумент командной строки дляclickhouse-client.param_<name>=<value>— как параметр строки запроса URL для HTTP-интерфейса.
На параметр запроса можно ссылаться в запросе с помощью {<name>: <datatype>}, где <name> — имя параметра запроса, а <datatype> — тип данных, к которому он приводится.
Пример с командой SET
Например, следующий SQL определяет параметры с именами a, b, c и d — каждый со своим типом данных:
SET param_a = 13;
SET param_b = 'str';
SET param_c = '2022-08-04 18:30:53';
SET param_d = {'10': [11, 12], '13': [14, 15]};
SELECT
{a: UInt32},
{b: String},
{c: DateTime},
{d: Map(String, Array(UInt8))};
13 str 2022-08-04 18:30:53 {'10':[11,12],'13':[14,15]}Пример с clickhouse-client
Если вы используете clickhouse-client, параметры указываются в виде --param_name=value. Например, следующий параметр имеет имя message и извлекается как String:
clickhouse-client --param_message='hello' --query="SELECT {message: String}"
helloЕсли параметр запроса представляет имя базы данных, таблицы, функции или другого идентификатора, используйте Identifier в качестве его типа. Например, следующий запрос возвращает строки из таблицы uk_price_paid:
SET param_mytablename = "uk_price_paid";
SELECT * FROM {mytablename:Identifier};Пример с HTTP-интерфейсом
Параметры запроса можно передавать как параметры строки запроса URL с префиксом param_. Например:
curl -s "http://localhost:8123/?param_message=hello" --data-binary "SELECT {message: String}"
helloПример с веб-интерфейсом
Встроенный веб-интерфейс (play.html) автоматически обнаруживает плейсхолдеры параметров {name:Type} в запросе и отображает для каждого параметра подписанное поле ввода. Значения параметров включаются в HTTP-запрос, а также сохраняются в URL страницы для добавления в закладки и общего доступа.
Функции
Вызовы функций записываются в виде идентификатора со списком аргументов (возможно, пустым) в ().
В отличие от стандартного SQL, скобки обязательны даже при пустом списке аргументов.
Например:
now()Также есть:
Некоторые агрегатные функции могут содержать в скобках два списка аргументов. Например:
quantile (0.9)(x) Эти агрегатные функции называются "параметрическими", а аргументы в первом списке — "параметрами".
Операторы
Операторы преобразуются в соответствующие функции при разборе запроса с учетом их приоритета и ассоциативности.
Например, выражение
1 + 2 * 3 + 4преобразуется в
plus(plus(1, multiply(2, 3)), 4)`Типы данных и движки таблиц базы данных
Типы данных и движки таблиц в запросе CREATE записываются так же, как идентификаторы и функции.
Иными словами, они могут как содержать список аргументов в скобках, так и не содержать его.
Дополнительные сведения см. в разделах:
Выражения
Выражением может быть любое из следующего:
- функция
- идентификатор
- литерал
- применение оператора
- выражение в скобках
- подзапрос
- звёздочка
Оно также может содержать псевдоним.
Список выражений — это одно или несколько выражений, разделённых запятыми. Функции и операторы, в свою очередь, могут использовать выражения в качестве аргументов.
Константное выражение — это выражение, результат которого известен во время анализа запроса, то есть до выполнения. Например, выражения, состоящие из литералов, являются константными выражениями.
Псевдонимы выражений
Псевдоним — это заданное пользователем имя для выражения в запросе.
expr AS aliasНиже поясняются элементы приведённого выше синтаксиса.
| Часть синтаксиса | Описание | Пример | Примечания |
|---|---|---|---|
AS |
Ключевое слово для задания псевдонимов. Псевдоним для имени таблицы или имени столбца в предложении SELECT можно задать и без ключевого слова AS. |
SELECT table_name_alias.column_name FROM table_name table_name_alias. |
В функции CAST ключевое слово AS имеет другое значение. См. описание функции. |
expr |
Любое выражение, поддерживаемое ClickHouse. | SELECT column_name * 2 AS double FROM some_table |
|
alias |
Имя для expr. Псевдонимы должны соответствовать синтаксису идентификаторов. |
SELECT "table t".column_name FROM table_name AS "table t". |
Примечания по использованию
- Псевдонимы действуют глобально в пределах запроса или подзапроса, и вы можете определить псевдоним для любого выражения в любой части запроса. Например:
SELECT (1 AS n) + 2, n`.- Псевдонимы недоступны в подзапросах и между ними. Например, при выполнении следующего запроса ClickHouse возвращает исключение
Unknown identifier: num:
`SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a`- Если для результирующих столбцов в предложении
SELECTподзапроса задан псевдоним, эти столбцы видны во внешнем запросе. Например:
SELECT n + m FROM (SELECT 1 AS n, 2 AS m)`.- Будьте осторожны с псевдонимами, совпадающими с именами столбцов или таблиц. Рассмотрим следующий пример:
CREATE TABLE t
(
a Int,
b Int
)
ENGINE = TinyLog();
SELECT
argMax(a, b),
sum(b) AS b
FROM t;
Received exception from server (version 18.14.17):
Code: 184. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: Aggregate function sum(b) is found inside another aggregate function in query.В предыдущем примере мы объявили таблицу t со столбцом b.
Затем при выборке данных мы задали псевдоним sum(b) AS b.
Поскольку псевдонимы являются глобальными,
ClickHouse подставил в выражение argMax(a, b) выражение sum(b) вместо литерала b.
Эта подстановка привела к Исключению.
Звёздочка
В запросе SELECT звёздочка может заменять выражение.
Подробнее см. в разделе SELECT.
Комментарии