查询
CREATE DATABASE
创建 ‘db_name’ 数据库.
CREATE DATABASE [IF NOT EXISTS]
一个数据库是库中表的一个目录.如果包含“IF NOT EXISTS” ,此数据库已经存在,则查询将不返回错误。
CREATE TABLE
CREATE TABLE 查询有几种形式.
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ...) ENGINE = engine
在数据库 ‘db’ 中或者当前的数据库中如果 ‘db’没有被设置(结构被指定在引擎中)创建一个表名为 ‘name’ 的表,表的结构是一个列描述的列表. If 如果索引通过引擎来支持, 他们将被提示作为表引擎的参数.
列描述是最简单的名称类型.例如:RegionID UInt32. 对于默认值,表达式能够被定义.
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name AS [db2.]name2 [ENGINE = engine]
创建一个表与另外一个表有相同的结构.你能为表指定不同的引擎.如果引擎没有被指定,相同的引擎将被用在 ‘db2.name2’ 表上.
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ...
创建一个表,其结构类似SELECT查询语句的结果,带有'engine'引擎,使用Select结果来填充数据.
如果 IF NOT EXISTS 被指定,如果表存在,则查询不返回任何错误。在这种情况下,查询不做任何事情.
默认值
列描述符能够指定一个表达式为一个默认值,方式之一是: DEFAULT 表达式, MATERIALIZED 表达式, ALIAS 表达式. 例如: URLDomain String DEFAULT domain(URL).
如果一个表达式的默认值没有被定义, 对于数值型,默认值将被设置为0, 对于字符串,默认值将被设置为空字符串;对于数组将被设置为空数组;日期将被设置为0000-00-00 或者or 0000-00-00 00:00:00. NULLs 不支持.
如果默认表达式被定义, 列类型是可选的. 如果没有一个显式的类型,默认的表达式类型将被使用.例如: EventDate DEFAULT toDate(EventTime) - ‘Date’ 类型将被用于‘EventDate’ 列.
如果数据类型和默认表达式被定义.此表达式将被转换到特定的类型,使用类型转换函数. 例如: Hits UInt32 DEFAULT 0 与下面是等价的 Hits UInt32 DEFAULT toUInt32(0).
默认表达式能够被定义作为任意的表达式,表的常量或列.当创建和更改表结构时, 它将检查表达式不包含 loops. 对于 INSERT语句, 它将检查表达式是可解析的 - 参数传递之后,所有的列能够被计算.
DEFAULT 表达式
默认值. 如果INSERT 语句没有指定后续的列,他们将通过计算后续的表达式来填充.
MATERIALIZED 表达式
Materialized 表达式. 此列并不指定在INSERT语句中,因为他们经常被计算.对于一个INSERT 语句,没有列的列表,这些列不被考虑。另外,此列并不被替换,当在时SELECT查询中,使用asterisk. 这保持了使用SELECT * INSERT 插入表之后的不变性.
查询解析器
ClickHouse目前有两种解析器:一个是SQL解析器(递归下降分析器),一个数据格式解析器(快速流解析器),第二个仅用于INSERT语句。因此,Insert语句使用了上述两种解析器。
INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
INSERT INTO t VALUES部分使用SQL解析器,DATA部分使用流解析器。
数据可以使用任意格式,当接收到请求后,服务器将不超过’max_query_size’的部分放在RAM中解析(默认为1MB),剩余部分则使用流解析器。ClickHouse和MySQL一样,对于比较大的INSERT语句不存在解析和处理问题。
在INSERT语句中使用使用Values格式时,数据看上去和SELECT语句的表达式解析类似,但区别很大。Values的格式限制多很多。 接下来我们要讲SQL解析器,对于解析的更多信息,可以参考格式章节(https://clickhouse.yandex/reference_en.html#Formats) 。
空白行
语法结构之间可以有任意数量的空白字符,包括空格,缩进,换行,CR,form feed。
注释
可以编写SQL和C风格的注释: 从头到行尾,空格可以省略 C风格:从/到/,允许多行。
关键字
类似SELECT这种的关键字是大小写不敏感的,但和标准SQL不同,其他关键字(包括列名,方法名等)都是大小写敏感的。关键字使用的单词不是预留的,可以在其他命名中使用,它们仅在合适的上下文中被解析为关键字。
标识符
不用引号的标识符首字符必须是拉丁字符或下划线,后面可以是拉丁字符,下划线或数字,即满足如下的正则表达式:
^[a-zA-Z_][0-9a-zA-Z_]*$
例如这些是合法标识符:x,1, X_y__Z123
引号中的标识符:和MySQL一样,使用id的形式,中间可以使用任意字符,特殊字符(比如`符号本身)可以使用反斜杠转义。转义规则和字符串常量相同。推荐使用没有括号的标识符。
常量
包括数字常量,字符串常量和混合常量
数字常量
数字常量的解析方式顺序:
—首先使用64位有符号数,使用'strtoull'方法
—如果不成功,则使用64位无符号数,'strtoll'方法
—如果不成功,则使用浮点数,'strtod'方法
—最后不成功则返回错误
响应数据则使用结果能够适应的最小类型,例如1使用Uint8,而256使用Uint16,更多信息则请参考上一篇文章 ClickHouse 数据类型介绍 。
字符串常量
字符串常量必须使用单引号。特殊字符可以使用反斜杠转义。转义字符都带有特殊的含义:
\b, \f, \r, \n, \t, \0, \a, \v, \xHH
其他情况下,对于任意字符c,\c被转成字符c本身,也就是\表示\,'表示'字符。
混合常量
数组中支持混合常量和元组,混合常量示例:[1, 2, 3],元组示例:(1, 'Hello, world!', 2)
事实上他们不是常量,而是使用了数组构造函数或元组构造函数的表达式。更多信息参考操作符Operators
数组至少包含一个元素,元组至少包含两个元素。元组是专门用于匹配IN语句中的SELECT请求结果的。通常情况下不能被存储到数据库中(Memory表除外)。
函数
函数就是一个标识符,后面跟着一串(可能为空)用小括号括起来的参数。和标准的SQL不同,即使是空的参数列表,也必须带有括号,比如now()。
函数分为普通函数和聚合函数。某些聚合函数带有两组参数,比如quantile(0.9)(x)。这种聚合函数被称为参数化函数,第一组参数就是参数化方法中的参数。对于没有带参数的聚合方法语法就和普通方法一样。
运算符
运算符在解析请求时会被转换为对应的函数,同时考虑运算符的优先级和结合性,例如 1+2+3+4被转换为 plus(plus(1, multiply(2, 3)), 4),更多内容请参考操作符
数据类型和表引擎
在CREATE语句中,数据类型和表引擎写法和标识符及函数一致。也就是说他们可能含有参数列表,也可能不含。更多信息参考上一篇文章ClickHouse 数据类型介绍。
别名
在SELECT请求中,表达式可以使用AS语法指定别名。表达式在AS左边,别名在AS右边。和标准SQL不同,别名不仅仅可以在表达式顶层指定,也可以用在任意的语句中。例如:
表达式
表达式具体为函数,标识符,常量,运算符等,包在括号中,放在子查询内。表达式可以包含别名。多个表达式使用逗号分隔。函数和运算符也可以使用表达式作为参数。
SQL语句操作
DDL::Create Database
CREATE DATABASE [IF NOT EXISTS] db_name
创建一个名为db_name的数据库,数据库仅仅是一系列表的目录。指定了 IF NOT EXISTS后,如果数据库已经存在,语句不会返回错误。
DDL::Create Table
建表请求有多种格式
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = engine
在DB数据库中简历一个name的表,如果没有指定DB则使用当前数据库。结构是括号中指定的格式,引擎使用指定的引擎。表结构是所有列描述的列表。如果引擎支持索引,可以在引擎的参数中指定。
列描述最简单的情况是"name type",例如RegionID UInt32,也可以用表达式指定默认值,下面会有详细说明。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name AS [db2.]name2 [ENGINE = engine]
这个语句使用另一个表的结构建表,如果没有指定引擎,则会使用db2.name2的引擎。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ...
上面的语句使用SELECT的结果作为结构建表,同时把SELECT结果的数据插入表中,引擎可以单独指定。
在所有上面的方法中,IF NOT EXISTS可以发挥作用。
默认值
列可以使用下面的语法指定一个默认值:DEFAULT expr, MATERIALIZED expr, ALIAS expr。 例如URLDomain String DEFAULT domain(URL)
如果没有指定默认值,对于数字会默认为0,字符串为空字符串,数组为空数组,日期为0000-00-00,时间为0000-00-00 00:00:00,不支持NULL如果指定了默认值,类型可以省略,这种情况下使用默认值的类型,例如EventDate DEFAULT toDate(EventTime),EventDate的类型会被设置为Date。
如果明确指定了类型,默认值的会被转换为目标类型。 例如Hits UInt32 DEFAULT 0 和 Hits UInt32 DEFAULT toUInt32(0)是完全相同的。默认值的表达式中也可以使用表常量和其他列。建表和修改表结构时,会检查是否包含循环,INSERT语句中,会检查是否表达式中的所有列都已经传入了。
"DEFAULT expr"表示普通的默认值,如果INSERT语句没有指定相应列,会自动使用表达式的值填充
"MATERIALIZED expr"表示“物化列”,这样的列不允许在INSERT中指定,因为它必须计算得来。对于没有指定列的INSERT语句,这些列不会被考虑在内,使用SELECT *时,这些列也不会返回,因为将SELECT * 的结果使用不指定列的INSERT语句插入式很常见的行为,会引发问题。
"ALIAS expr"是指别名,这样的列根本不会被存储起来。不能够使用INSERT插入数据,也不会包括在SELECT * 的返回值中,如果别名是在请求解析阶段展开的,可以使用在SELECT语句中。
当使用ALTER请求来添加新列时,这些列的老数据不会被写入。当读取的老数据没有这些新列的值时,默认情况下会在线计算。然而如果这些表达式使用了没有在SELECT语句中出现的列,这些列会被读取到,当然仅在需要的情况下。
如果向表中添加了新列,但是后来又修改了默认表达式,老数据的值将会更新。当运行后台合并操作时,原来没有值的列会在合并后被写入。不允许为嵌套的数据结构设置默认值。
临时表
如果指定了TEMPORARY,将会创建临时表,临时表的特征:
连接结束或中断时,临时表会消失。
临时表只能使用内存引擎,其它引擎不支持临时表。
临时表不能指定数据库,他们是在数据库外创建的。
如果临时表和普通表的名字相同,请求没有指定DB的名字时,临时表会被使用。
在分布式处理中,一个请求用到的临时表会传给远程的服务器。
通常情况下,临时表不是手动建立的,而是使用外部数据,或使用分布式的IN语句时来自动创建。