目录
一、增
二、删改
三、查询以及各种子句
1、with子句
a、表达式为常量
b、表达式为函数调用
c、表达式为子查询
2、from子句
3、array join子句
a、INNER ARRAY JOIN
b、LEFT ARRAY JOIN
c、数组的一些函数
groupArray
groupUniqArray
arrayFlatten
splitByChar
arrayJoin
arrayMap
嵌套类型
4、 join子句
连接精度
连接类型
注意事项
5、WHERE 与 PREWHERE 子句
6,GROUP BY 子句
WITH ROLLUP
WITH CUBE
WITH TOTALS
7、having子句
8、ORDER BY子句
NULLS LAST
NULLS FIRST
9、limit by 子句
10、 limit 子句
11、select和distinct子句
一、增
INSERT语句支持三种语法范式
第一种使用values格式
-- 中括号表示里面的内容可以省略
INSERT INTO [db.]table_name [(col1, col2, col3...)] VALUES (val1, val2, val3, ...), (val1, val2, val3, ...), ...
使用 VALUES 格式的语法写入数据时,还支持加入表达式或函数,例如:
INSERT INTO partizion_v2 VALUES('matsuri', toString(1+2), now())
第二种使用自定格式的语法:
INSERT INTO [db.]table_name [(col1, col2, col3...)] FORMAT format_name data_set
例子如下
INSERT INTO partition_v2 FORMAT CSV \'mea', 'www.mea.com', '2019-01-01''nana', 'www.nana.com', '2019-02-01''matsuri', 'www.matsuri.com', '2019-03-01'
第三种使用select子句形式的语法:
INSERT INTO [db.]table_name [(col1, col2, col3...)] SELECT ...
二、删改
ClickHouse 不是以事务为中心的数据库系统,它主要设计用于在线分析处理(OLAP)场景,强调的是高性能的读取和聚合查询,而不是复杂的事务处理。因此,ClickHouse 不支持传统意义上的 DELETE 和 UPDATE 操作,也不支持事务特性。
然而,ClickHouse 提供了一种称为 Mutation 的机制,允许用户进行类似 DELETE 和 UPDATE 的操作。Mutation 语句的执行是一个异步的后台过程,语句被提交之后就会立即返回。所以这并不代表具体逻辑已经执行完毕,它的具体执行进度需要通过 system.mutations 系统表查询
Mutation 是通过 ALTER TABLE 语句实现的,使用方法例如:
DELETE 语句的完整语法如下所示:
ALTER TABLE [db_name.]table_name DELETE WHERE filter_expr
案例如下:删除id=xxx的
ALTER TABLE partition_v2 DELETE WHERE ID ='xxx'
UPDATE 支持在一条语句中同时定义多个修改字段,但是分区键和主键不能作为修改字段。修改语句如下:
ALTER TABLE [db_name.]table_name UPDATE column1 = expr1 [, ...] WHERE filter_expr
三、查询以及各种子句
注意:clickhouse对于sql语句的解析是大小写敏感的,ClickHouse 的类型也大小写敏感,比如:UInt8 不可以写成 uint8,String 不可以写成 string; 但关键字大小写不敏感,例如select a 和select A 意义不同,但是min max等大小写不敏感
1、with子句
格式为:with 表达式 as var
作用就是增加可读性和可维护性
一个子句可以为多个表达式起名例如:WITH 1 AS a, 2 AS b SELECT a + b;
a、表达式为常量
常量可以是整数,字符串,浮点数,甚至数组,都可以
使用方法例如:
with 10 as startselect number from system.numbers where number>start limit 7
b、表达式为函数调用
WITH SUM(data_uncompressed_bytes) AS bytes
SELECT database, formatReadableSize(bytes) AS format
FROM system.columns
GROUP BY database
ORDER BY bytes DESC/*
┌─database─┬─format───┐
│ system │ 5.32 GiB │
│ default │ 0.00 B │
└──────────┴──────────┘
*/
如果不使用 WITH 子句,那么 SELECT 里面出现的就是 formatReadableSize(SUM(data_uncompressed_bytes)),这样读起来不是很方便,所以使用 WITH 子句将里面的聚合函数调用起一个名字叫 bytes,那么后面的查询直接使用 bytes 即可。
c、表达式为子查询
-- SELECT sum(data_uncompressed_bytes) FROM system.columns 会得到一个数值
-- 因此本质上和表达式为常量是类似的,只不过多了一个计算的过程
WITH (SELECT sum(data_uncompressed_bytes) FROM system.columns) AS total_bytes
SELECT database, (sum(data_uncompressed_bytes) / total_bytes) * 100 AS database_disk_usage
FROM system.columns
GROUP BY database
ORDER BY database_disk_usage DESC
/*
┌─database─┬─database_disk_usage─┐
│ system │ 100 │
│ default │ 0 │
└──────────┴─────────────────────┘
*/
注意:表达式只能返回的数据不能超过 1 行,否则会抛出异常,且不可以放在from后面作为临时表使用,如果需要多个值可以放在一个容器(列表、集合、字典等等)里面
而postgresql是可以返回任何数据,行数不限,并且可以放在from后面当临时表,
与postgresql的命名也不同,clickhouse别名在as后面,postgresql在as前面
2、from子句
from子句表示从何处读取数据,目前支持3种形式
从数据表中读取SELECT name FROM people从子查询中读取SELECT max_id FROM (SELECT max(id) AS max_id FROM people)从表函数中读取SELECT number FROM numbers(N) -- 会返回 0 到 N - 1
3、array join子句
首先造一个包含array数组字段的测试表
CREATE TABLE t1 (title String,value Array(UInt8)
) ENGINE = Memory();-- 然后写入数据
INSERT INTO t1 VALUES ('food', [1, 2, 3]), ('fruit', [3, 4]), ('meat', []);-- 查询
SELECT * FROM t1;
/*
┌─title─┬─value───┐
│ food │ [1,2,3] │
│ fruit │ [3,4] │
│ meat │ [] │
└───────┴─────────┘
*/
在一条 SELECT 语句中,只能存在一个 ARRAY JOIN(使用子查询除外),目前支持 INNER 和 LEFT 两种 JOIN 策略:
a、INNER ARRAY JOIN
ARRAY JOIN 在默认情况下使用的是 INNER JOIN 策略,例如下面的语句:
SELECT title, value FROM t1 ARRAY JOIN value;
/*
┌─title─┬─value─┐
│ food │ 1 │
│ food │ 2 │
│ food │ 3 │
│ fruit │ 3 │
│ fruit │ 4 │
└───────┴───────┘
*/
从查询结果可以发现,最终的数据基于 value 数组被展开成了多行,并且排除掉了空数组,同时会自动和其它字段进行组合(相当于按行合并)。在使用 ARRAY JOIN 时,如果还想访问展开前的数组字段,那么只需为原有的数组字段添加一个别名即可,例如:
-- 如果不给 ARRAY JOIN 后面的 value 起一个别名,那么 value 就是展开后的结果
-- 如果给 ARRAY JOIN 后面的 value 起一个别名 val,那么 value 就还是展开前的数组字段
-- 而 val 才是展开后的结果,所以再反过来,让 val 出现在 SELECT 中即可
SELECT title, value, val FROM t1 ARRAY JOIN value AS val;
/*
┌─title─┬─value───┬─val─┐
│ food │ [1,2,3] │ 1 │
│ food │ [1,2,3] │ 2 │
│ food │ [1,2,3] │ 3 │
│ fruit │ [3,4] │ 3 │
│ fruit │ [3,4] │ 4 │
└───────┴─────────┴─────┘
*/
我们看到 ClickHouse 的确是当之无愧的最强 OLAP 数据库,不单单是速度快,最重要的是,它提供的查询语法也很方便。如果你用过 Hive 的话,会发现这里特别像里面的 lateral view explode 语法。
b、LEFT ARRAY JOIN
ARRAY JOIN 子句支持 LEFT 连接策略,例如执行下面的语句:
SELECT title, value, val FROM t1 LEFT ARRAY JOIN value AS val;
/*
┌─title─┬─value───┬─val─┐
│ food │ [1,2,3] │ 1 │
│ food │ [1,2,3] │ 2 │
│ food │ [1,2,3] │ 3 │
│ fruit │ [3,4] │ 3 │
│ fruit │ [3,4] │ 4 │
│ meat │ [] │ 0 │
└───────┴─────────┴─────┘
*/
在改为 LEFT 连接查询后,可以发现,在 INNER JOIN 中被排除掉的空数组出现在了返回的结果集中。但此时的 val 是零值。
c、数组的一些函数
有如下表
SELECT * FROM t2;
/*
┌─────────dt─┬─cash───────┐
│ 2020-01-01 │ [10,10,10] │
│ 2020-01-02 │ [20,20,20] │
│ 2020-01-01 │ [10,10,10] │
│ 2020-01-02 │ [20,20] │
│ 2020-01-03 │ [] │
│ 2020-01-03 │ [30,30,30] │
└────────────┴────────────┘
*/
groupArray
它是把多行数据合并成一个数组,相当于是聚合函数的一种
SELECT dt, groupArray(cash) FROM t2 GROUP BY dt;
/*
┌─────────dt─┬─groupArray(cash)────────┐
│ 2020-01-01 │ [[10,10,10],[10,10,10]] │
│ 2020-01-02 │ [[20,20,20],[20,20]] │
│ 2020-01-03 │ [[],[30,30,30]] │
└────────────┴─────────────────────────┘
*/
groupUniqArray
在组合的时候会对元素进行去重
SELECT dt, groupUniqArray(cash) FROM t2 GROUP BY dt;
/*
┌─────────dt─┬─groupUniqArray(cash)─┐
│ 2020-01-01 │ [[10,10,10]] │
│ 2020-01-02 │ [[20,20],[20,20,20]] │
│ 2020-01-03 │ [[],[30,30,30]] │
└────────────┴──────────────────────┘
*/
arrayFlatten
类似于flatmap,将数组扁平化
SELECT dt, groupArray(cash),arrayFlatten(groupArray(cash)) FROM t2 GROUP BY dt;
SELECT dt, groupUniqArray(cash) FROM t2 GROUP BY dt;
/*
┌─────────dt─┬─groupUniqArray(cash)─┐─arrayFlatten(groupArray(cash))─┐
│ 2020-01-01 │ [[10,10,10],[10,10,10]] │ [10,10,10,10,10,10] │
│ 2020-01-02 │ [[20,20],[20,20,20]] │ [20,2020,20,20] │
│ 2020-01-03 │ [[],[30,30,30]] │ [30,30,30] │
└────────────┴──────────────────────------------┘──────────────────────┘
*/
splitByChar
将字符串按照指定字符分割成数组:
SELECT splitByChar('^', 'komeiji^koishi');
/*
┌─splitByChar('^', 'komeiji^koishi')─┐
│ ['komeiji','koishi'] │
└────────────────────────────────────┘
*/
arrayJoin
该函数和 ARRAY JOIN 子句的作用非常类似:
SELECT * FROM t1;
/*
┌─title─┬─value───┐
│ food │ [1,2,3] │
│ fruit │ [3,4] │
│ meat │ [] │
└───────┴─────────┘
*/select title ,arrayjoin(value) from t1;/*
┌─title─┬─arrayjoin(value)─┐
│ food │ 1 │
│ food │ 2 │
│ food │ 3 │
│ fruit │ 3 │
│ fruit │ 4 │
└───────┴───────┘
*/
arrayMap
对数组中的每一个元素都以相同的规则进行映射:
-- arrayMap(x -> x * 2, value) 表示将 value 中的每一个元素都乘以 2,然后返回一个新数组
-- 而 mapV 就是变换过后的新数组,直接拿来用即可
SELECT title, arrayMap(x -> x * 2, value) AS mapV, v
FROM t1 LEFT ARRAY JOIN mapV as v
/*
┌─title─┬─mapV────┬─v─┐
│ food │ [2,4,6] │ 2 │
│ food │ [2,4,6] │ 4 │
│ food │ [2,4,6] │ 6 │
│ fruit │ [6,8] │ 6 │
│ fruit │ [6,8] │ 8 │
│ meat │ [] │ 0 │
└───────┴─────────┴───┘
*/-- 另外展开的字段也可以不止一个
SELECT title, arrayMap(x -> x * 2, value) AS mapV, v,value, v_1
FROM t1 LEFT ARRAY JOIN mapV as v, value AS v_1
/*
┌─title─┬─mapV────┬─v─┬─value───┬─v_1─┐
│ food │ [2,4,6] │ 2 │ [1,2,3] │ 1 │
│ food │ [2,4,6] │ 4 │ [1,2,3] │ 2 │
│ food │ [2,4,6] │ 6 │ [1,2,3] │ 3 │
│ fruit │ [6,8] │ 6 │ [3,4] │ 3 │
│ fruit │ [6,8] │ 8 │ [3,4] │ 4 │
│ meat │ [] │ 0 │ [] │ 0 │
└───────┴─────────┴───┴─────────┴─────┘
*/
嵌套类型
在写入嵌套数据类型时,记得同一行数据中各个数组的长度需要对齐,而对多行数据之间的数组长度没有限制,否则会报错:如下
CREATE TABLE t3(title String,nested Nested(v1 UInt32,v2 UInt64)
) ENGINE = Log();-- 接着写入测试数据
-- 在写入嵌套数据类型时,记得同一行数据中各个数组的长度需要对齐,而对多行数据之间的数组长度没有限制
INSERT INTO t3
VALUES ('food', [1, 2, 3], [10, 20, 30]),('fruit', [4, 5], [40, 50]),('meat', [], [])
INSERT INTO t3
VALUES ('food', [1, 2, 3], [10, 20, 30,40]),('fruit', [4, 5], [40, 50]),('meat', [], [])当数组大小不同时如上,会报错,当然不同行的数组大小可以不同,例如food和fruit
SQL 错误 [190]: ClickHouse exception, code: 190, host: 192.168.81.15, port: 8123; Code: 190. DB::Exception: Elements 'nested.v1' and 'nested.v2' of Nested data structure 'nested' (Array columns) have different array sizes. (SIZES_OF_ARRAYS_DOESNT_MATCH) (version 22.1.3.7 (official build))
对嵌套类型数据的访问,ARRAY JOIN 既可以直接使用字段列名:
-- nested 只有 v1 和 v2
-- 所以 ARRAY JOIN nested.v1, nested.v2 等价于 ARRAY JOIN nested
SELECT title, nested.v1, nested.v2 FROM t3 ARRAY JOIN nested.v1, nested.v2
/*
┌─title─┬─nested.v1─┬─nested.v2─┐
│ food │ 1 │ 10 │
│ food │ 2 │ 20 │
│ food │ 3 │ 30 │
│ fruit │ 4 │ 40 │
│ fruit │ 5 │ 50 │
└───────┴───────────┴───────────┘
*/
嵌套类型也支持 ARRAY JOIN 部分嵌套字段,可以看到,在这种情形下,只有被 ARRAY JOIN 的数组才会展开。
SELECT title, nested.v1, nested.v2 FROM t3 ARRAY JOIN nested.v1
/*
┌─title─┬─nested.v1─┬─nested.v2──┐
│ food │ 1 │ [10,20,30] │
│ food │ 2 │ [10,20,30] │
│ food │ 3 │ [10,20,30] │
│ fruit │ 4 │ [40,50] │
│ fruit │ 5 │ [40,50] │
└───────┴───────────┴────────────┘
*/
在查询嵌套类型时也能够通过别名的形式访问原始数组:
SELECT title, nested.v1, nested.v2, n.v1, n.v2
from t3 ARRAY JOIN nested AS n;
/*
┌─title─┬─nested.v1─┬─nested.v2──┬─n.v1─┬─n.v2─┐
│ food │ [1,2,3] │ [10,20,30] │ 1 │ 10 │
│ food │ [1,2,3] │ [10,20,30] │ 2 │ 20 │
│ food │ [1,2,3] │ [10,20,30] │ 3 │ 30 │
│ fruit │ [4,5] │ [40,50] │ 4 │ 40 │
│ fruit │ [4,5] │ [40,50] │ 5 │ 50 │
└───────┴───────────┴────────────┴──────┴──────┘
*/
4、 join子句
JOIN 子句可以对左右两张表的数据进行连接,它的语法包含连接精度和连接类型两部分。
连接精度
连接精度决定了 JOIN 查询在连接数据时所使用的策略,目前支持 ALL、ANY 和 ASOF 三种类型(还有两种类型SEMI 和 ANTI只能用在left join和right join上面)。如果不主动声明,则默认是 ALL
举个例子有如下表数据:
SELECT * FROM tbl_1;
/*
┌─id─┬─code1─┬─count─┐
│ 1 │ A001 │ 30 │
│ 2 │ A002 │ 28 │
│ 3 │ A003 │ 32 │
└────┴───────┴───────┘
*/SELECT * FROM tbl_2;
/*
┌─id─┬─code2─┬─count─┐
│ 1 │ B001 │ 35 │
│ 1 │ B001 │ 29 │
│ 3 │ B003 │ 31 │
│ 4 │ B004 │ 38 │
└────┴───────┴───────┘
*/
下面进行测试
SELECT t1.id, t1.code1, t2.code2
FROM tbl_1 AS t1
ALL INNER JOIN tbl_2 AS t2
ON t1.id = t2.id;
/*
┌─id─┬─code1─┬─code2─┐
│ 1 │ A001 │ B001 │
│ 1 │ A001 │ B001 │
│ 3 │ A003 │ B003 │
└────┴───────┴───────┘
*/
-- 一切正常,跟一般的关系型数据库是类似的,但如果将 ALL 改成 ANY
SELECT t1.id, t1.code1, t2.code2
FROM tbl_1 AS t1
ANY INNER JOIN tbl_2 AS t2
ON t1.id = t2.id;
/*
┌─id─┬─code1─┬─code2─┐
│ 1 │ A001 │ B001 │
│ 3 │ A003 │ B003 │
└────┴───────┴───────┘
*/
除了 ALL 和 ANY 之外还有一个 ASOF,ALL 还是 ANY,在连接的时候必须是等值连接。但 ASOF 表示模糊连接,例如 t1.id >= t2.id 例子如下
SELECT t1.id, t1.code1, t2.code2, t1.count AS count1, t2.count AS count2
FROM tbl_1 AS t1
ASOF INNER JOIN tbl_2 AS t2
ON t1.id = t2.id AND t1.count > t2.count;
/*
┌─id─┬─code1─┬─code2─┬─count1─┬─count2─┐
│ 1 │ A001 │ B001 │ 30 │ 29 │
│ 3 │ A003 │ B003 │ 32 │ 31 │
└────┴───────┴───────┴────────┴────────┘
*/
SEMI 和 ANTI
我们之前说连接精度不止 ALL、ANY、ASOF 三种,还有 SEMI 和 ANTI,只不过这两个比较特殊,因为它们只能用在 LEFT JOIN 和 RIGHT JOIN 上面,所以我们单独介绍。
t1 SEMI LEFT JOIN t2 USING(id):遍历 t1 中的 id,如果存在于 t2 中,则输出
t1 SEMI RIGHT JOIN t2 USING(id):遍历 t2 中的 id,如果存在于 t1 中,则输出
t1 ANTI LEFT JOIN t2 USING(id):遍历 t1 中的 id,如果不存在于 t2 中,则输出
t1 ANTI RIGHT JOIN t2 USING(id):遍历 t2 中的 id,如果不存在于 t1 中,则输出
这个 SEMI 的功能貌似有些重复了,因为我们使用 ALL 和 ANY 完全可以取代。其实如果你用过 hive 的话,会发现 SEMI LEFT JOIN 和 ANTI LEFT JOIN 是 IN/EXISTS 的一种更加高效的实现:
结论:
1,如果左表内的一行数据,在右表中有多行数据与之连接匹配,那么当连接精度为 ALL,会返回右表中全部连接的数据;
2,当连接精度为 ANY,会仅返回右表中第一行连接的数据
3,如果连接精度为 ASOF,那么允许在等值连接条件后面追加一个非等值连接,所以上面的 t1.id = t2.id 是等值连接,t1.count > t2.count 是非等值连接。但需要注意的是:使用非等值连接时,这个非等值可以是 >、>=、<、<=,但不能是 !=;并且对于 ASOF 而言,连接条件必须是等值连接和非等值连接的组合,两者缺一不可。
连接类型
-- 省略连接精度,默认为 ALL
-- 左连接
SELECT t1.id, t1.code1, t2.code2
FROM tbl_1 t1 LEFT JOIN tbl_2 t2
USING(id); -- 等价于 t1.id = t2.id
/*
┌─id─┬─code1─┬─code2─┐
│ 1 │ A001 │ B001 │
│ 1 │ A001 │ B001 │
│ 2 │ A002 │ │
│ 3 │ A003 │ B003 │
└────┴───────┴───────┘
*/-- 右连接
SELECT t1.id, t1.code1, t2.code2
FROM tbl_1 t1 RIGHT JOIN tbl_2 t2
USING(id);
/*
┌─id─┬─code1─┬─code2─┐
│ 1 │ A001 │ B001 │
│ 1 │ A001 │ B001 │
│ 3 │ A003 │ B003 │
└────┴───────┴───────┘
┌─id─┬─code1─┬─code2─┐
│ 4 │ │ B004 │
└────┴───────┴───────┘
*/-- 全连接
SELECT t1.id, t1.code1, t2.code2
FROM tbl_1 t1 FULL JOIN tbl_2 t2
USING(id);
/*
┌─id─┬─code1─┬─code2─┐
│ 1 │ A001 │ B001 │
│ 1 │ A001 │ B001 │
│ 2 │ A002 │ │
│ 3 │ A003 │ B003 │
└────┴───────┴───────┘
┌─id─┬─code1─┬─code2─┐
│ 4 │ │ B004 │
└────┴───────┴───────┘
*/
和关系型数据库类似,但有一点区别,就是当没有与之匹配的记录时,会使用对应类型的空值进行补全,而不是 Null。这里没有指定连接精度,默认为 ALL
注意事项
最后,还有两个关于 JOIN 查询的注意事项。
1. 关于性能
最后,还有两个关于 JOIN 查询的注意事项。为了能够优化 JOIN 查询性能,首先应该遵循左大右小的原则,即数据量小的表要放在右侧。这是因为在执行 JOIN 查询时,无论使用的是哪种连接方式,右表都会被全部加载到内存中与左表进行比较。
其次,JOIN 查询目前没有缓存的支持,这意味着每一次 JOIN 查询,即便是连续执行相同的 SQL,也都会生成一次全新的执行计划。如果应用程序会大量使用 JOIN 查询,则需要进一步考虑借助上层应用侧的缓存服务或使用 JOIN 表引擎来改善性能。
最后,如果是在大量维度属性补全的查询场景中,则建议使用字典代替 JOIN 查询。因为在进行多表的连接查询时,查询会转换成两两连接的形式,而这种滚雪球式的查询很可能带来性能问题。
2. 空值策略
在之前的介绍中,连接查询的空值(那些未被连接的数据)是由默认值填充的,这与其他数据库所采取的策略不同(由Null 填充)。连接查询的空值策略通过 join_use_nulls 参数指定的,默认为 0。当参数值为 0 时,空值由数据类型的默认值填充;而当参数值为 1 时,空值由 Null 填充。
5、WHERE 与 PREWHERE 子句
除了 WHERE,ClickHouse 还支持全新的 PREWHERE 子句,PREWHERE 目前只能用于 MegeTee 系列的表引擎,它可以看作对是 WHERE 的一种优化,其作用与 WHERE 相同,均是用来过滤数据。但它们的不同之处在于。使用 PREWHERE 时,首先只会读取 PREWHERE 指定的列字段数据,用于数据过滤的条件判断。待数据过滤之后再读取 SELECT 声明的列字段以补全其余属性。所以在一些场合下,PREWHERE 相比 WHERE 而言,处理的数据量更少,性能更高。
既然 WHERE 子句性能更优,那么是否需要将所有的 WHERE 子句都替换成 PREWHERE 子句呢?其实大可不必,因为 ClickHouse 实现了自我优化的功能,会在条件合适的情况下将 WHERE 替换为 PREWHERE。如果想开启这项特性,只需要将 optimize_move_to_prewhere 设置为 1 即可,当然默认就为 1,即开启状态。
6,GROUP BY 子句
聚合查询能配合 WITH ROLLUP、WITH CUBE、WITH TOTALS 三种修饰符获取额外的汇总信息
造测试表
CREATE TABLE sales_data(product String,channel String,amount int) ENGINE = Log();-- 接着写入测试数据
INSERT INTO sales_data
VALUES ('桔子', '淘宝', 248175),('香蕉', '淘宝', 252148),('苹果', '店面', 246198),('香蕉', '店面', 256602),('桔子', '店面', 245029),('苹果', '淘宝', 252908),('苹果', '京东', 252057),('桔子', '京东', 251795),('香蕉', '京东', 245904)
WITH ROLLUP
GROUP BY 子句加上 WITH ROLLUP 选项时,首先按照全部的分组字段进行分组汇总;然后从右往左依次去掉一个分组字段再进行分组汇总,被去掉的字段显示为零值;最后,将所有的数据进行一次汇总,所有的分组字段都显示为零值。
select product,channel,sum(amount)
from sales_data
group by product,channel
with rollup查询结果如下桔子 淘宝 248175
香蕉 淘宝 252148
苹果 店面 246198
香蕉 店面 256602
桔子 店面 245029
苹果 淘宝 252908
苹果 京东 252057
桔子 京东 251795
香蕉 京东 245904
香蕉 754654
桔子 744999
苹果 7511632250816
我们注意到,多了四条数据,上面三条,就是按照 product、channel 汇总之后,再单独按 product 汇总,而此时会给对应的 channel 设为零值(这里是空字符串,关系型数据库中为 Null)。同理最后一条数据是全量汇总,不需要指定 product 和 channel,所以显示为 product 和 channel 都显示为零值。我们看到这就相当于按照 product 单独聚合然后再自动拼接在上面了,排好序,并且自动将 channel 赋值为零值,同理最后一条数据也是如此
WITH CUBE
CUBE 代表立方体,它用于对分组字段进行各种可能的组合,能够产生多维度的交叉统计结果,CUBE 通常用于数据仓库中的交叉报表分析
select product,channel,sum(amount)
from sales_data
group by product,channel
with CUBE 桔子 淘宝 248175
香蕉 淘宝 252148
苹果 店面 246198
香蕉 店面 256602
桔子 店面 245029
苹果 淘宝 252908
苹果 京东 252057
桔子 京东 251795
香蕉 京东 245904
香蕉 754654
桔子 744999
苹果 751163淘宝 753231京东 749756店面 7478292250816
CUBE 返回了更多的分组数据,其中不仅包含了 ROLLUP 汇总的结果,还包含了相当于按照 channel 进行聚合的记录。因此随着分组字段的增加,CUBE 产生的组合将会呈指数级增长
WITH TOTALS
只包含一个全局汇总的结果
select product,channel,sum(amount)
from sales_data
group by product,channel
with TOTALS桔子 淘宝 248175
香蕉 淘宝 252148
苹果 店面 246198
香蕉 店面 256602
桔子 店面 245029
苹果 淘宝 252908
苹果 京东 252057
桔子 京东 251795
香蕉 京东 2459042250816
7、having子句
HAVING 子句要和 GROUP BY 子句同时出现,不能单独使用
select product,channel,sum(amount) as cnt
from sales_data
group by product,channel
having cnt>250000香蕉 淘宝 252148
香蕉 店面 256602
苹果 淘宝 252908
苹果 京东 252057
桔子 京东 251795
8、ORDER BY子句
在 MergeTree 表引擎中也有 ORDER BY 参数用于指定排序键,这个的作用域是分区内,所以当查询时如果有多个分区就不保证顺序了,所以需要order by。在使用时可以定义多个排序键,每个排序键后需紧跟ASC 或者DESC,不写默认为asc
例如
SELECT * FROM tbl ORDER BY v1 ASC, v2 DESC;
SELECT * FROM tbl ORDER BY v1, v2 DESC;
NULLS LAST
null值排在最后,无论升序还是降序
写法如下
select arrayJoin([1,22,NULL,3,-1]) as v order by v asc-1
1
3
22
NULL
NULLS FIRST
null值排在第一,无论升序还是降序,写法如上
9、limit by 子句
LIMIT BY 运行于 ORDER BY 之后和 LIMIT 之前,它能够按照指定分组,最多返回前 n 行数据(少于 n 行则按照实际数量返回),常用于 分组TOP N 的查询场景。LIMIT BY 语法规则如下:
表数据
香蕉 店面 256602
苹果 淘宝 252908
香蕉 淘宝 252148
苹果 京东 252057
桔子 京东 251795
桔子 淘宝 248175
苹果 店面 246198
香蕉 京东 245904
桔子 店面 245029
select product,channel,sum(amount) as cnt
from sales_data
group by product,channel
order by cnt desc
limit 1 by channel香蕉 店面 256602
苹果 淘宝 252908
苹果 京东 252057
LIMIT BY 也可以指定偏移量,因为不一定从一条开始选择,而指定偏移量有两种方式:
- 一种方式如上:limit 条数 by 维度
- 另一种方式:limit 条数,偏移量(从第几条开始)by 维度 案例如下
select product,channel,sum(amount) as cnt
from sales_data
group by product,channel
order by cnt desc
limit 1 ,1 by channel香蕉 淘宝 252148
桔子 京东 251795
苹果 店面 246198
10、 limit 子句
用法有三种如下
LIMIT N
LIMIT N OFFSET M
LIMIT M, N
limit 与limit by的区别
11、select和distinct子句
支持正则查询,例如下面会选择以 n 开头和包含字母 p 的字段:
SELECT COLUMNS('^n'), COLUMNS('p') FROM system.databases
distinct和group by 执行后虽然结果相同,但是distinct的执行计划更简单,而且在不使用order by子句时,distinct在limit n满足条件时立刻结束查询,后面的就不查了,group by会分组执行完后在limit