1. 结果
1.1 版本23.6.1.1524
SQL Dynamic Column Selection with ClickHouse
REPLACE子句在SELECT语句中位于COLUMNS选择之后,但在WHERE子句之前。这意味着REPLACE操作会先于WHERE过滤执行。
此外,文档还指出:
"We can see that those two columns have both been replaced and the other columns are as they were in the previous query."
这进一步说明REPLACE操作是在整个结果集上执行的,而不是在WHERE过滤之后。
因此,我们可以得出结论,在ClickHouse中,查询结果集字段的动态替换(REPLACE)的优先级高于WHERE子句的过滤操作。REPLACE会先执行,然后才是WHERE过滤。
1.2 版本24.3
24.3版本:将where和replace的执行逻辑合并到投影表达式中,所以不会出现这种冲突现象
2. 不同版本测试过程
2.1 23.6版本
ck-test :)
CREATE TABLE default.students
(
`id` UInt32,
`name` String,
`age` UInt8
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/default/students', '{replica}')
ORDER BY id
SETTINGS index_granularity = 8192
;
CREATE TABLE default.students
(
`id` UInt32,
`name` String,
`age` UInt8
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/default/students', '{replica}')
ORDER BY id
SETTINGS index_granularity = 8192
Query id: 70fece67-c230-47c0-b638-0a1d878d328d
Ok.
0 rows in set. Elapsed: 0.104 sec.
ck-test :)
insert into students values(1,'Loe',18);
INSERT INTO students FORMAT Values
Query id: f534c2a2-c919-4ed0-854b-3f1351127589
Ok.
1 row in set. Elapsed: 0.021 sec.
ck-test :) select * from students;
SELECT *
FROM students
Query id: d45396c8-d2d5-4cc8-bbeb-da3be6b64849
┌─id─┬─name─┬─age─┐
│ 1 │ Loe │ 18 │
└────┴──────┴─────┘
1 row in set. Elapsed: 0.003 sec.
ck-test :)
SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: 22ab5c71-8725-4a7c-b99f-85ef070fe723
Ok.
0 rows in set. Elapsed: 0.002 sec.
ck-test :) explain plan SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
EXPLAIN
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: 36fd8fd7-8a15-44bc-998b-8f531d176a4b
┌─explain─────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│ Filter (WHERE) │
│ ReadFromMergeTree (default.students) │
└─────────────────────────────────────────────┘
3 rows in set. Elapsed: 0.002 sec.
ck-test :) explain syntax SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
EXPLAIN SYNTAX
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: 1e76e5f8-6053-4100-a423-4366a486bac7
┌─explain────────────┐
│ SELECT │
│ id, │
│ 'LoL' AS name, │
│ age │
│ FROM students │
│ WHERE 0 │
└────────────────────┘
6 rows in set. Elapsed: 0.002 sec.
2.2 24.3版本
localhost :) SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: 900bebff-2c32-4186-80cd-00828a74089f
┌─id─┬─name─┬─age─┐
1. │ 12 │ LoL │ 31 │
└────┴──────┴─────┘
1 row in set. Elapsed: 0.004 sec.
localhost :) explain plan SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
EXPLAIN
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: c184e4e2-92a6-46a7-ad46-332b5df62bef
┌─explain───────────────────────────────────┐
1. │ Expression ((Project names + Projection)) │
2. │ Expression │
3. │ ReadFromMergeTree (default.students) │
└───────────────────────────────────────────┘
3 rows in set. Elapsed: 0.002 sec.
localhost :) explain syntax SELECT * REPLACE ('LoL' AS name) FROM students where name = 'Leo';
EXPLAIN SYNTAX
SELECT * REPLACE 'LoL' AS name
FROM students
WHERE name = 'Leo'
Query id: 1908bf5f-1c50-4f67-a39b-07360de52e87
┌─explain────────────┐
1. │ SELECT │
2. │ id, │
3. │ 'LoL' AS name, │
4. │ age │
5. │ FROM students │
6. │ WHERE 0 │
└────────────────────┘
6 rows in set. Elapsed: 0.003 sec.
2.3 核心不同
23.6
┌─explain─────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│ Filter (WHERE) │
│ ReadFromMergeTree (default.students) │
└─────────────────────────────────────────────┘
24.3
┌─explain───────────────────────────────────┐
1. │ Expression ((Project names + Projection)) │
2. │ Expression │
3. │ ReadFromMergeTree (default.students) │
└───────────────────────────────────────────┘
上面测试过程使用到了一些clickhouse的基础指令,下面给出相关使用介绍。
Prewhere
PREWHERE
只能在 *MergeTree 家族树里使用;
1. WHERE子句:
- WHERE子句在查询中是最后执行的,它作用于从表中读取的所有数据。
- WHERE子句可以包含任意条件,并且可以使用各种函数和操作符进行数据筛选。
- WHERE子句可以使用索引来加速查询,优化性能。
2. PREWHERE子句:
- PREWHERE子句在WHERE子句之前执行,它作用于从数据源读取的数据。
- PREWHERE子句通常用于过滤数据源中不必要的行,以减少读取和处理的数据量,提升性能。
- PREWHERE子句只能包含简单的条件,不能使用聚合函数、多个列的条件判断等复杂操作。
- PREWHERE子句不能使用索引来加速查询。
EXPLAIN Types
AST
— Abstract syntax tree.SYNTAX
— Query text after AST-level optimizations.QUERY TREE
— Query tree after Query Tree level optimizations.PLAN
— Query execution plan.PIPELINE
— Query execution pipeline.
可添加参数:
PLAN: 用于查看执行计划,可以指定五个参数;
header:打印计划中各个步骤的 header 说明,默认 0(关闭);
description:打印计划中各个步骤的描述,默认 1(开启);
indexes:显示索引使用情况,默认 0(关闭);
actions:打印计划中各个步骤的详细信息,默认 0(关闭);
json:以 JSON 格式打印执行计划的详细信息,默认 0(关闭);
23.6版本/24.3版本
ck-test :) explain plan header=1,actions =1 select * replace ('LOL' as name) from students where name = 'Leo';
EXPLAIN header = 1, actions = 1
SELECT * REPLACE 'LOL' AS name
FROM students
WHERE name = 'Leo'
Query id: d9baf4a5-102e-4c28-b04f-c467fe44c11b
┌─explain──────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│ Header: id UInt32 │
│ name String │
│ age UInt8 │
│ Actions: INPUT :: 0 -> id UInt32 : 0 │
│ INPUT :: 1 -> age UInt8 : 1 │
│ COLUMN Const(String) -> name String : 2 │
│ Positions: 0 2 1 │
│ Filter (WHERE) │
│ Header: id UInt32 │
│ age UInt8 │
│ Filter column: 0 (removed) │
│ Actions: INPUT :: 0 -> id UInt32 : 0 │
│ INPUT :: 1 -> age UInt8 : 1 │
│ COLUMN Const(UInt8) -> 0 UInt8 : 2 │
│ Positions: 0 1 2 │
│ ReadFromMergeTree (default.students) │
│ Header: id UInt32 │
│ age UInt8 │
│ ReadType: Default │
└──────────────────────────────────────────────────┘
20 rows in set. Elapsed: 0.002 sec.
ck-test :) select * from students;
SELECT *
FROM students
Query id: d60b6fbf-fb18-4e9f-b909-862c0adfdce0
┌─id─┬─name─┬─age─┐
│ 1 │ Leo │ 18 │
└────┴──────┴─────┘
1 row in set. Elapsed: 0.002 sec.
上面是旧版 的详细计划,我们来看看新版 24.3
localhost :) explain plan header=1,actions =1 select * replace ('LOL' as name) from students where name = 'Leo';
EXPLAIN header = 1, actions = 1
SELECT * REPLACE 'LOL' AS name
FROM students
WHERE name = 'Leo'
Query id: 3e0e03cc-eb8c-4e5b-9a15-f1560be77bf4
┌─explain────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ Expression ((Project names + Projection)) │
2. │ Header: id UInt32 │
3. │ name String │
4. │ age UInt8 │
5. │ Actions: INPUT : 0 -> __table1.id UInt32 : 0 │
6. │ INPUT : 1 -> __table1.age UInt8 : 1 │
7. │ COLUMN Const(String) -> name String : 2 │
8. │ ALIAS __table1.id :: 0 -> id UInt32 : 3 │
9. │ ALIAS __table1.age :: 1 -> age UInt8 : 0 │
10. │ Positions: 3 2 0 │
11. │ Expression │
12. │ Header: __table1.id UInt32 │
13. │ __table1.age UInt8 │
14. │ Actions: INPUT : 0 -> id UInt32 : 0 │
15. │ INPUT : 1 -> age UInt8 : 1 │
16. │ INPUT :: 2 -> name String : 2 │
17. │ ALIAS id :: 0 -> __table1.id UInt32 : 3 │
18. │ ALIAS age :: 1 -> __table1.age UInt8 : 0 │
19. │ Positions: 3 0 │
20. │ ReadFromMergeTree (default.students) │
21. │ Header: name String │
22. │ id UInt32 │
23. │ age UInt8 │
24. │ ReadType: Default │
25. │ Parts: 1 │
26. │ Granules: 1 │
27. │ Prewhere info │
28. │ Need filter: 1 │
29. │ Prewhere filter │
30. │ Prewhere filter column: equals(__table1.name, 'Leo'_String) (removed) │
31. │ Actions: INPUT : 0 -> name String : 0 │
32. │ COLUMN Const(String) -> 'Leo'_String String : 1 │
33. │ ALIAS name : 0 -> __table1.name String : 2 │
34. │ FUNCTION equals(__table1.name :: 2, 'Leo'_String :: 1) -> equals(__table1.name, 'Leo'_String) UInt8 : 3 │
35. │ Positions: 0 3 │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
35 rows in set. Elapsed: 0.002 sec.
明显发现 where 在24.3 的版本ReadFromMergeTree (default.students) (读取阶段)就已经执行了,就不会向23.6版本,一样造成歧义。
以上是作者在实际情况中发现的一点,在官方文档中并没有找到相关的修复说明,有人找到的话,望分享~~ 有不足之处,请指正