clickhouse学习笔记(五)SQL操作

news2024/9/19 10:56:53

目录

一、增

二、删改

三、查询以及各种子句

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 start

select 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
苹果		    751163
		        2250816

        我们注意到,多了四条数据,上面三条,就是按照 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
	    店面	747829
		        2250816

        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
香蕉	京东	245904
		        2250816

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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1908873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小米订单锐减背后的挑战与应对之道

近期&#xff0c;富士康印度子公司Bharat FIH面临高管离职、工厂关闭的困境&#xff0c;其背后原因之一是小米订单的显著下滑&#xff0c;据报道&#xff0c;这一降幅高达70%。这一现象不仅反映了富士康在印度市场的艰难处境&#xff0c;也揭示了小米在全球智能手机市场面临的挑…

Atom CMS v2.0 SQL 注入漏洞(CVE-2022-24223)

前言 概要 CVE-2022-24223 是一个发现于 Atom CMS v2.0 中的 SQL 注入漏洞。该漏洞存在于 /admin/login.php 文件中&#xff0c;通过该文件&#xff0c;攻击者可以在未经身份验证的情况下执行任意的 SQL 命令。 漏洞描述 该漏洞位于 Atom CMS 的管理员登录页面&#xff08;/a…

甄选范文“论区块链技术及应用”,软考高级论文,系统架构设计师论文

论文真题 区块链作为一种分布式记账技术,目前已经被应用到了资产管理、物联网、医疗管理、政务监管等多个领域。从网络层面来讲,区块链是一个对等网络(Peer to Peer, P2P),网络中的节点地位对等,每个节点都保存完整的账本数据,系统的运行不依赖中心化节点,因此避免了中…

跨境电商代购系统与电商平台API结合的化学反应

随着全球化的不断推进和互联网技术的飞速发展&#xff0c;跨境电商已成为国际贸易的重要组成部分。跨境电商代购系统作为连接国内外消费者与商品的桥梁&#xff0c;不仅为消费者提供了更多元化的购物选择&#xff0c;也为商家开辟了更广阔的市场空间。在这一过程中&#xff0c;…

数据结构——顺序表(java实现)

文章目录 顺序表顺序表的定义代码实现&#xff1a;创建一个顺序表的类在顺序表中增加一条新的数据展示顺序表中内容在pos位置处插入一条数据判断顺序表中是否包含指定的数据查找某个数据在顺序表中的位置获取pos位置的元素将pos位置的元素改为value删除顺序表中第一个出现的数据…

搭建基础库~

前言 项目中会用到工具库、函数库以及一些跟框架绑定的组件&#xff0c;如果这些基础模块每个项目都实现一套&#xff0c;维护起来那真的头大&#xff0c;你说呢&#x1f609; 搭建流程 准备工作 创建文件夹myLib、安装Git以及pnpm 目录大概就系这样子&#xff1a; myLib ├…

从零开始做题:好怪哦

题目 给出一个压缩文件 解题 方法1 01Edit打开&#xff0c;发现是个反着的压缩包&#xff08;末尾倒着的PK头&#xff09; import os# 目标目录路径 # target_directory /home/ai001/alpaca-lora# 切换到目标目录 # os.chdir(target_directory)# 打印当前工作目录以确认…

# Redis 入门到精通(一)数据类型(1)

Redis 入门到精通&#xff08;一&#xff09;数据类型&#xff08;1&#xff09; 一 、Redis 入门到精通 基本介绍 1、Redis 基础 ( windows 环境 ) redis 入门数据类型通用命令Jedis 2、Redis 高级 ( linux 环境 ) 持久化redis.conf 配置事务集群 3、Redis 应用 ( linux…

WAIC | 2024年世界人工智能大会“数学与人工智能”学术会议成功举办!

由斯梅尔数学与计算研究院&#xff08;Smale Institue of Mathematics & Computation&#xff09;主办的2024年世界人工智能大会(WAIC)“数学与人工智能”学术会议7月4日在上海世博中心圆满落幕&#xff01;作为全球性高级别学术研讨会&#xff0c;此次会议由华院计算技术&…

SQLServer的系统数据库用别的服务器上的系统数据库替换后做跨服务器连接时出现凭证、非对称金钥或私密金钥的资料无效

出错作业背景&#xff1a; 公司的某个sqlserver服务器要做迁移&#xff0c;由于该sqlserver服务器上数据库很多&#xff0c;并且做了很多的job和维护计划&#xff0c;重新安装的sqlserver这些都是空的&#xff0c;于是就想到了把系统4个系统数据库进行替换&#xff0c;然后也把…

Camera Raw:裁剪

Camera Raw 的裁剪 Crop面板提供了裁剪、旋转、翻转、拉直照片等功能&#xff0c;通过它们可以更精确地调整照片的视角和范围&#xff0c;以达到最佳二次构图的视觉效果。 快捷键&#xff1a;C ◆ ◆ ◆ 使用方法与技巧 1、使用预设 选择多种裁剪预设&#xff08;如 1:1、16:…

JAVA基础-----128陷阱

一、何为128陷阱 Java中Integer类型在使用比较时的特殊行为------128陷阱&#xff0c;解释了当数值在-128到127范围内&#xff0c;由于valueOf方法的缓存机制导致地址相同&#xff0c;比较为真&#xff1b;超出这个范围则新分配内存&#xff0c;地址不同&#xff0c;比较为假。…

YOLOv10改进 | 主干篇 | 低照度增强网络PE-YOLO改进主干(改进暗光条件下的物体检测模型)

一、本文介绍 本文给大家带来的改进机制是低照度图像增强网络PE-YOLO中的PENet&#xff0c;PENet通过拉普拉斯金字塔将图像分解成多个分辨率的组件&#xff0c;增强图像细节和低频信息。它包括一个细节处理模块&#xff08;DPM&#xff09;&#xff0c;用于通过上下文分支和边…

数据链路层(超详细)

引言 数据链路层是计算机网络协议栈中的第二层&#xff0c;位于物理层之上&#xff0c;负责在相邻节点之间的可靠数据传输。数据链路层使用的信道主要有两种类型&#xff1a;点对点信道和广播信道。点对点信道是指一对一的通信方式&#xff0c;而广播信道则是一对多的通信方式…

办公必备——ONLYOFFICE8.1版本桌面编辑器

一、介绍ONLYOFFICE ONLYOFFICE是一款免费的开源办公软件&#xff0c;它可以让你创建、编辑和分享文档、表格和演示文稿。就像微软的Office一样&#xff0c;但它是完全免费的&#xff0c;而且可以在多种设备上使用&#xff0c;包括电脑和手机。它还支持多人同时在线编辑文档&am…

Golang | Leetcode Golang题解之第223题矩形面积

题目&#xff1a; 题解&#xff1a; func computeArea(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2 int) int {area1 : (ax2 - ax1) * (ay2 - ay1)area2 : (bx2 - bx1) * (by2 - by1)overlapWidth : min(ax2, bx2) - max(ax1, bx1)overlapHeight : min(ay2, by2) - max(ay1, by1)…

独立开发者系列(22)——API调试工具apifox的使用

接口的逻辑已经实现&#xff0c;需要对外发布接口&#xff0c;而发布接口的时候&#xff0c;我们需要能自己简单调试接口。当然&#xff0c;其实自己也可以写简单的代码调试自己的接口&#xff0c;因为其实就是简单的request请求或者curl库读取&#xff0c;调整请求方式get或者…

第11章 规划过程组(二)(11.10制订进度计划)

第11章 规划过程组&#xff08;二&#xff09;11.10制订进度计划&#xff0c;在第三版教材第395~397页&#xff1b;文字图片音频方式 第一个知识点&#xff1a;定义及作用 分析活动顺序、持续时间、资源需求和进度制约因素&#xff0c;创建项目进度模型&#xff0c;从而落实项目…

基于单片机的太阳能热水器控制系统设计

随着我国经济水平的不断提高&#xff0c;民众对生活质量的追求也在不断进步&#xff0c;对于现代家庭而言&#xff0c;热水器成为了必备的生活电器。目前市面上的电器主要是电热水器、燃气热水器以及太阳能热水器。就能源节约性能而言&#xff0c;太阳能热水器占据了绝对优势&a…

01day C++初入学习

这里写目录标题 1.C区别于C的输入输出2.什么是命名空间3. namespace的定义namespace的使用(1)namespace嵌套使用(2)多⽂件中可以定义同名namespace(3) 4.命名空间的使用5.C输⼊&输出6.缺省参数7.函数重载8.引用8.1引用的特性8.3引用的使用 1.C区别于C的输入输出 #include&…