跳至主要内容

语法

系统中存在两种类型的解析器:完整的 SQL 解析器(递归下降解析器)和数据格式解析器(快速流解析器)。除了 INSERT 查询之外,所有情况下都只使用完整的 SQL 解析器。INSERT 查询同时使用两种解析器。

INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')

INSERT INTO t VALUES 部分由完整解析器解析,数据 (1, 'Hello, world'), (2, 'abc'), (3, 'def') 由快速流解析器解析。您也可以使用 input_format_values_interpret_expressions 设置,将完整解析器用于数据解析。当 input_format_values_interpret_expressions = 1 时,ClickHouse 首先尝试使用快速流解析器解析值。如果失败,ClickHouse 会尝试使用完整解析器解析数据,将其视为 SQL 表达式

数据可以采用任何格式。收到查询后,服务器会计算请求在内存中不超过 max_query_size 字节(默认情况下为 1 MB),其余部分将通过流解析。这有助于避免大型 INSERT 查询带来的问题。

INSERT 查询中使用 Values 格式时,可能看起来数据与 SELECT 查询中的表达式解析方式相同,但事实并非如此。Values 格式更加有限。

本文其余部分介绍完整的解析器。有关格式解析器的更多信息,请参阅 格式 部分。

空格

语法结构之间(包括查询的开头和结尾)可以包含任意数量的空格符号。空格符号包括空格、制表符、换行符、回车符和换页符。

注释

ClickHouse 支持 SQL 风格和 C 风格的注释。

  • SQL 风格的注释以 --#!# 开头,并持续到行尾,--#! 后面的空格可以省略。
  • C 风格的注释从 /**/,可以跨越多行,空格也不必。

关键字

当关键字对应时,关键字不区分大小写

  • SQL 标准。例如,SELECTselectSeLeCt 都是有效的。
  • 一些流行的 DBMS(MySQL 或 Postgres)中的实现。例如,DateTime 等同于 datetime

您可以在 system.data_type_families 表中检查数据类型名称是否区分大小写。

与标准 SQL 不同,所有其他关键字(包括函数名称)都 **区分大小写**。

关键字不是保留字;它们只在相应的上下文中被视为关键字。如果使用与关键字相同的名称的 标识符,请将它们用双引号或反引号括起来。例如,查询 SELECT "FROM" FROM table_name 有效,如果表 table_name 包含名为 "FROM" 的列。

标识符

标识符是

  • 集群、数据库、表、分区和列名称。
  • 函数。
  • 数据类型。
  • 表达式别名.

标识符可以是带引号或不带引号的。建议使用不带引号的标识符。

不带引号的标识符必须匹配正则表达式 ^[a-zA-Z_][0-9a-zA-Z_]*$,并且不能与 关键字 相同。例如:x_1X_y__Z123_

如果您想使用与关键字相同的标识符,或者您想在标识符中使用其他符号,请使用双引号或反引号将其括起来,例如:"id"`id`

字面量

有数字、字符串、复合和 NULL 字面量。

数字

数字字面量按如下方式解析:

  • 首先,使用 strtoull 函数,将其解析为 64 位有符号数。
  • 如果解析失败,则使用 strtoll 函数,将其解析为 64 位无符号数。
  • 如果解析失败,则使用 strtod 函数,将其解析为浮点数。
  • 否则,返回错误。

字面量值将转换为最小的能容纳该值的数据类型。例如,1 将解析为 UInt8,而 256 将解析为 UInt16。有关更多信息,请参阅 数据类型。数字字面量中的下划线 _ 将被忽略,可以用于提高可读性。

支持以下数字字面量:

整数110_000_0001844674407370955161501 小数0.1 指数表示法 - 1e100-1e-100 浮点数123.456infnan

十六进制0xc0fe SQL 标准兼容十六进制字符串x'c0fe'

二进制0b1101 SQL 标准兼容二进制字符串 - b'1101'

不支持八进制字面量,以避免意外的解释错误。

字符串

字符串字面量必须用单引号括起来,不支持双引号。转义可以使用以下两种方式:

  • 使用前面的单引号,其中单引号字符 '(仅此字符)可以转义为 '',或者
  • 使用前面的反斜杠以及以下支持的转义序列:\\\'\b\f\r\n\t\0\a\v\xHH。如果反斜杠位于除上述字符之外的字符之前,则反斜杠将失去其特殊含义,即会被按字面量解释。

在字符串字面量中,您需要使用转义代码 \'(或:'')和 \\ 转义 '\

复合

数组使用方括号 [1, 2, 3] 构造。元组使用圆括号 (1, 'Hello, world!', 2) 构造。从技术上讲,这些不是字面量,而是分别使用数组创建运算符和元组创建运算符的表达式。数组必须至少包含一个元素,元组必须至少包含两个元素。在 SELECT 查询的 IN 子句中出现元组时,有一个单独的情况。查询结果可以包含元组,但元组无法保存到数据库中(除了使用 Memory 引擎的表)。

NULL

表示值缺失。

要将 NULL 存储在表字段中,该字段必须是 Nullable 类型。

根据数据格式(输入或输出),NULL 可能有不同的表示方式。有关更多信息,请参阅 数据格式 文档。

处理 NULL 存在许多细微差别。例如,如果比较运算的至少一个参数为 NULL,则该运算的结果也为 NULL。乘法、加法和其他运算也是如此。有关更多信息,请阅读每个运算的文档。

在查询中,可以使用 IS NULLIS NOT NULL 运算符以及相关的 isNullisNotNull 函数检查 NULL

Heredoc

heredoc 是一种定义字符串(通常是多行字符串)的方式,同时保留原始格式。heredoc 被定义为自定义字符串字面量,放置在两个 $ 符号之间,例如 $heredoc$。两个 heredoc 之间的值将“按原样”处理。

可以使用 heredoc 嵌入 SQL、HTML 或 XML 代码片段等。

示例

查询

SELECT $smth$SHOW CREATE VIEW my_view$smth$;

结果

┌─'SHOW CREATE VIEW my_view'─┐
│ SHOW CREATE VIEW my_view │
└────────────────────────────┘

定义和使用查询参数

查询参数允许您编写包含抽象占位符而不是具体标识符的通用查询。当执行包含查询参数的查询时,所有占位符将被解析并替换为实际的查询参数值。

定义查询参数有两种方式:

  • 使用 SET param_<name>=<value> 命令
  • 在命令行上使用 --param_<name>='<value>' 作为 clickhouse-client 的参数。<name> 是查询参数的名称,<value> 是其值。

可以使用 {<name>: <datatype>} 在查询中引用查询参数,其中 <name> 是查询参数名称,<datatype> 是要转换为的数据类型。

例如,以下 SQL 定义了名为abcd 的参数,每个参数都有不同的数据类型。

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,则参数指定为--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};
注意

查询参数不是可以在任意 SQL 查询中的任意位置使用的通用文本替换。它们主要设计为在SELECT 语句中代替标识符或文字。

函数

函数调用写成一个标识符,后面跟着圆括号中的参数列表(可能为空)。与标准 SQL 相反,即使是空参数列表,括号也是必需的。例如:now()。有常规函数和聚合函数(请参见聚合函数部分)。一些聚合函数可以在括号中包含两个参数列表。例如:quantile (0.9) (x)。这些聚合函数称为“参数”函数,第一个列表中的参数称为“参数”。无参数聚合函数的语法与常规函数相同。

运算符

运算符在查询解析期间转换为其对应的函数,同时考虑其优先级和结合性。例如,表达式1 + 2 * 3 + 4 将转换为plus(plus(1, multiply(2, 3)), 4)

数据类型和数据库表引擎

CREATE 查询中的数据类型和表引擎的写法与标识符或函数相同。换句话说,它们可能包含或不包含括号中的参数列表。有关更多信息,请参见数据类型表引擎CREATE部分。

表达式别名

别名是查询中表达式的用户定义名称。

expr AS alias
  • AS — 用于定义别名的关键字。您可以使用或不使用AS关键字在SELECT 子句中定义表名或列名的别名。

    例如,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

  • aliasexpr 的名称。别名应符合标识符语法。

    例如,SELECT "table t".column_name FROM table_name AS "table t"

使用说明

别名对于查询或子查询是全局的,您可以在查询的任何部分为任何表达式定义别名。例如,SELECT (1 AS n) + 2, n

别名在子查询和子查询之间不可见。例如,在执行查询SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a 时,ClickHouse 会生成异常Unknown identifier: num

如果为子查询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.

在此示例中,我们声明了具有列b的表t。然后,在选择数据时,我们定义了sum(b) AS b 别名。由于别名是全局的,ClickHouse 将表达式argMax(a, b) 中的字面量b 替换为表达式sum(b)。这种替换导致了异常。您可以通过将prefer_column_name_to_alias 设置为1 来更改此默认行为。

星号

SELECT 查询中,星号可以代替表达式。有关更多信息,请参见SELECT部分。

表达式

表达式是函数、标识符、文字、运算符的应用、括号中的表达式、子查询或星号。它还可以包含别名。表达式列表是一个或多个由逗号分隔的表达式。函数和运算符反过来可以将表达式作为参数。