SELECT 语句可用于执行数据检索。 默认情况下,请求的数据返回给客户端,同时结合 INSERT INTO可以被转发到不同的表。
所有子句都是可选的,但紧接在 SELECT 后面的必需表达式列表 expr_list 除外。
[WITH expr_list|(subquery)] SELECT [DISTINCT] expr_list [FROM [db.]table | (subquery) | table_function] [FINAL] [SAMPLE sample_coeff] [ARRAY JOIN ...] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI] JOIN (subquery)|table (ON <expr_list>)|(USING <column_list>) [PREWHERE expr] [WHERE expr] [GROUP BY expr_list] [WITH TOTALS] [HAVING expr] [ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [LIMIT [offset_value, ]n BY columns] [LIMIT [n, ]m] [WITH TIES] [UNION ALL ...] [settingsClause] [INTO OUTFILE filename] [FORMAT format]
SELECT [DISTINCT] expr_list
参数 | 说明 |
|---|---|
DISTINCT | 可选参数,用于去重查询结果中的重复行。在处理 |
| 可在查询的任意位置替代表达式,执行查询时会自动扩展为表中所有列的列表(不包含 |
AS 别名 | 可为查询结果中的列指定自定义名称,增强结果可读性。 |
在结果中包含所有列,请使用星号 (*)符号。 例如,
SELECT * FROM ...
将结果中包含 'a' 的列,可以使用 COLUMNS 表达。
SELECT COLUMNS('a') FROM table_name;
查询表中所有列名包含 'a' 的列。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.col_names ON CLUSTER sample_cluster ( aa Int8, ab Int8, bc Int8 ) ENGINE = MergeTree ORDER BY aa; -- 3. 插入测试数据 INSERT INTO sample_db.col_names VALUES (1, 2, 3),(4, 5, 6); -- 4. 查询操作 SELECT COLUMNS('a') FROM sample_db.col_names
输出结果:
┌─aa─┬─ab─┐ │ 1 │ 2 │ │ 4 │ 5 │ └────┴────┘
使用多个 COLUMNS 表达式并将函数应用于列。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.col_names ON CLUSTER sample_cluster ( aa Int8, ab Int8, bc Int8 ) ENGINE = MergeTree ORDER BY aa; -- 3. 插入测试数据 INSERT INTO sample_db.col_names VALUES (1, 2, 3),(4, 5, 6); -- 4. 查询操作 SELECT COLUMNS('a'), COLUMNS('c'), toTypeName(COLUMNS('c')) FROM sample_db.col_names
输出结果:
┌─aa─┬─ab─┬─bc─┬─toTypeName(bc)─┐ │ 1 │ 2 │ 3 │ Int8 │ │ 4 │ 5 │ 6 │ Int8 │ └────┴────┴────┴────────────────┘
返回的每一列 COLUMNS 表达式作为单独的参数传递给函数。 如果函数支持其他参数,您也可以将其他参数传递给函数。
使用 COLUMNS 表达式时,要保证传递给函数的参数数量和类型是函数所支持的,否则 ByteHouse 将报错。
例如,执行以下命令后,系统报错如下:
SELECT COLUMNS('a') + COLUMNS('c') FROM sample.col_names
Code: 42, Error: DB::Exception: Number of arguments for function plus doesn't match: passed 3, should be 2 SQLSTATE: 42000
该示例中,COLUMNS('a') 返回两列:aa 和 ab。COLUMNS('c') 返回 bc 列。 该 + 运算符不能应用于 3 个参数,因此 ByteHouse 抛出一个带有相关消息的异常。
匹配的列 COLUMNS 表达式可以具有不同的数据类型。
如果 COLUMNS 不匹配任何列,并且是在 SELECT 唯一的表达式,ByteHouse 将报错。
星号符号(*)使用场景:
您可以在查询的任何部分使用星号替代表达式。进行查询分析、时,星号将展开为所有表的列(不包括 MATERIALIZED 和 ALIAS 列)。 建议仅在以下场景使用星号:
LIMIT 1。但建议使用 DESC TABLE 查询。PREWHERE 进行强过滤时。Bytehouse 支持公用表表达式(Common table expression,CTE)。WITH 子句的结果可以在后续的 SELECT 查询中使用。但该功能存在一定的限制,包括:
WITH [columnExpr] AS identifier
参数 | 说明 |
|---|---|
columnExpr | 定义 CTE 的表达式(通常是子查询),生成临时结果集。 |
identifier | CTE 的名称(别名),用于在后续 |
WITH '2024-07-01 15:23:00' as ts_upper_bound SELECT * FROM system.tables WHERE metadata_modification_time <= ts_upper_bound LIMIT 5;
WITH sum(total_bytes) as s SELECT formatReadableSize(s), database FROM system.tables GROUP BY database ORDER BY s LIMIT 3;
/* 以下示例将返回最大的10个数据库。 */ WITH ( SELECT sum(total_bytes) FROM system.tables ) AS total_resoure_size SELECT (sum(total_bytes) / total_resoure_size) * 100 AS db_resource_usage, database FROM system.tables GROUP BY database ORDER BY db_resource_usage DESC LIMIT 10
WITH ['hello'] AS hello SELECT hello, * FROM ( WITH ['hello'] AS hello SELECT hello ) -- 结果 ┌─hello─────┬─hello_1─────┐ │ ['hello'] │ ['hello'] │ └───────────┴───────────┘
FROM 子句指定了读取数据的来源,支持设置为表、子查询、表函数。
从表中读取数据
FROM [tableIdentifier] [FINAL] [sampleClause]
从子查询中读取数据
FROM ([selectUnionStmt]) [FINAL] [sampleClause]
从表函数中读取数据
FROM tableFunctionExpr [FINAL] [sampleClause]
参数 | 说明 |
|---|---|
tableIdentifier | 指定表或视图的名称。 |
selectUnionStmt | 子查询语句,需使用括号包裹。 |
tableFunctionExpr | 表函数表达式。 |
FINAL | 当指定 |
sampleClause | 数据采样表达式(如 |
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.fromClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE=MergeTree() ORDER BY id; -- 3. 插入数据 INSERT INTO sample_db.fromClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 4. 查询表数据 SELECT * FROM sample_db.fromClause; -- 结果示例 ┌─id─┬─val─┐ 1. │ 1 │ 10 │ 2. │ 1 │ 11 │ 3. │ 1 │ 12 │ 4. │ 2 │ 20 │ 5. │ 2 │ 21 │ └────┴─────┘
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.fromClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE=MergeTree() ORDER BY id; -- 3. 插入数据 INSERT INTO sample_db.fromClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 4. 查询子查询数据 SELECT * FROM (SELECT * FROM sample_db.fromClause LIMIT 3); -- 结果示例 ┌─id─┬─val─┐ 1. │ 1 │ 10 │ 2. │ 1 │ 11 │ 3. │ 1 │ 12 │ └────┴─────┘
这里使用表函数 numbers 来生成一个包含 10 行的表。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.fromClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE=MergeTree() ORDER BY id; -- 3. 插入数据 INSERT INTO sample_db.fromClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 4. 查询表函数数据 SELECT * FROM numbers(10); -- 结果示例 ┌─number─┐ 1. │ 0 │ 2. │ 1 │ 3. │ 2 │ 4. │ 3 │ 5. │ 4 │ 6. │ 5 │ 7. │ 6 │ 8. │ 7 │ 9. │ 8 │ 10. │ 9 │ └────────┘
如果要使用采样,必须在创建 MergeTree 表时声明采样表达式,详见 SAMPLE BY 语句。
Sample 子句允许进行近似的 SELECT 查询处理。启用 sample 子句后,查询不会在所有数据上执行,而只会在某个数据片段上执行,适用于以下场景:
以下为数据采样的功能:
SELECT .. SAMPLE 查询始终返回相同结果。SAMPLE 子句支持以下语法:
SAMPLE 子句语法 | 描述 |
|---|---|
SAMPLE k | 按比例采样,k 表示抽取数据的比例,设置为从 0 到 1 的数字,支持小数和小数表示法。 例如, |
SAMPLE n | 按行数采样,n 表示最小采样行数,设置为足够大的整数。 |
SAMPLE k OFFSET m | 带偏移的比例采样,k 为采样比例,m 为偏移比例,均设置为从 0 到 1 的数字。例如, |
k 设置为从 0 到 1 的数字,支持小数和小数表示法。 例如,SAMPLE 1/2 或 SAMPLE 0.5。
SAMPLE K
在此示例中,将使用 10% 的数据进行近似计算。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.sampleClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree ORDER BY id SAMPLE BY id; -- 3. 插入数据 INSERT INTO sample_db.sampleClause SELECT * FROM numbers(1000); -- 4. 数据采样 SELECT COUNT() * 10 FROM sample_db.sampleClause SAMPLE 0.1; -- 查询 10% 数据
n 设置为足够大的整数。,例如,SAMPLE 10000000.
在这种情况下,查询在至少一个样本上执行 n 行(但不超过该值)。 例如,SAMPLE 10000000 表示在至少 10,000,000 行上运行查询。
SAMPLE N
在此示例中,将使用 2 行数据进行近似计算。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.sampleClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree ORDER BY id SAMPLE BY id; -- 3. 插入数据 INSERT INTO sample_db.sampleClause SELECT * FROM numbers(1000); -- 4. 数据采样 SELECT COUNT() FROM sample_db.sampleClause SAMPLE 2;
k 为采样比例,m 为偏移比例,均设置为从 0 到 1 的数字。例如,SAMPLE 0.2 OFFSET 0.3,表示从 30% 位置开始,取 20% 的数据区间。
SAMPLE K OFFSET M
在此示例中,跳过 20% 的数据后,将使用 10% 的数据进行近似计算。
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.sampleClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree ORDER BY id SAMPLE BY id; -- 3. 插入数据 INSERT INTO sample_db.sampleClause SELECT * FROM numbers(1000); -- 4. 数据采样 SELECT COUNT() FROM sample_db.sampleClause SAMPLE 0.1 OFFSET 0.2;
JOIN 子句通过使用每个表中共有的值,将一个或多个表的列组合在一起生成一个新表。
[ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER] JOIN [tableIdentifier] ON|USING [columnExprList]
参数 | 说明 |
|---|---|
ANY | 如果右表有多行匹配,则只连接第一行匹配结果。如果右表只有一行匹配,ANY 和 ALL 的结果相同。 |
ALL | 如果右表有多行匹配,ByteHouse 会从匹配的行中创建一个笛卡尔积。这是标准 SQL 中的正常 JOIN 行为。 |
ASOF | 用于连接具有不确定匹配的序列。 |
INNER JOIN | 只返回匹配的行。 |
LEFT OUTER JOIN | 除了返回匹配的行,还返回左表中不匹配的行。 |
RIGHT OUTER JOIN | 除了返回匹配的行,还返回右表中不匹配的行。 |
FULL OUTER JOIN | 除了返回匹配的行,还返回两个表中不匹配的行。 |
CROSS JOIN | 生成整个表的笛卡尔积,不指定“连接键”。 |
ON | USING
|
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.joinClause ON CLUSTER sample_cluster ( number UInt64 ) ENGINE=MergeTree ORDER BY number; -- 3. 插入数据 INSERT INTO sample_db.joinClause SELECT * FROM numbers(10); -- 4. 使用 JOIN 子句查询 SELECT number, joined FROM sample_db.joinClause ANY LEFT JOIN ( SELECT number * 2 AS number, number * 10 + 1 AS joined FROM sample_db.joinClause LIMIT 10 ) js2 USING number LIMIT 10 -- 结果示例 ┌─number─┬─joined─┐ │ 0 │ 1 │ │ 1 │ NULL │ │ 2 │ 21 │ │ 3 │ NULL │ │ 4 │ 41 │ │ 5 │ NULL │ │ 6 │ 61 │ │ 7 │ NULL │ │ 8 │ 81 │ │ 9 │ NULL │ └────────┴────────┘
对于包含数组列的表,数组连接(array join)可以生成一个新表,该新表的列包含初始列中每个单独的数组元素,同时重复复制其他列的值。
每个 SELECT 查询中只能指定一个 arrayJoin 子句。
[LEFT] ARRAY JOIN [columnExprList]
参数 | 说明 |
|---|---|
ARRAY JOIN | 仅展开非空数组,空数组所在行不会在结果中展示。 |
LEFT ARRAY JOIN |
|
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.arrayJoinClause ON CLUSTER sample_cluster ( s String, arr Array(UInt8) ) ENGINE = MergeTree ORDER BY s; -- 3. 插入数据 INSERT INTO sample_db.arrayJoinClause VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []); -- 4. 查询数据 SELECT s, arr FROM sample_db.arrayJoinClause ARRAY JOIN arr; -- 输出结果示例 ┌─s─────┬─arr─┐ │ Hello │ 1 │ │ Hello │ 2 │ │ World │ 3 │ │ World │ 4 │ │ World │ 5 │ └───────┴─────┘
Prewhere 子句是一种优化机制,用于更高效地应用过滤。即使未显式指定 PREWHERE 子句,系统也将自动将部分 WHERE 条件移动到 prewhere 阶段执行。PREWHERE 子句的作用仅在于当你认为自己比默认优化做得更好时,控制这种优化。
使用 prewhere 优化时,具体流程如下:
PREWHERE [columnExpr](http://columnexpr/)
-- 1. 创建表 CREATE TABLE sample.prewhereClause(id UInt32, val UInt32) ENGINE = MergeTree() ORDER BY id; -- 2. 插入数据 INSERT INTO sample.prewhereClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 3. 执行 prewhere 查询 SELECT * FROM sample.prewhereClause PREWHERE id=1 WHERE val>10; -- (1, 11), (1, 12) expected
WHERE 子句用于对 SELECT 语句中 FROM 子句所获取的数据进行过滤。若查询中包含 WHERE 子句,则该子句必须包含一个返回 UInt8 类型值的表达式,通常为包含比较运算符和逻辑运算符的表达式。表达式求值结果为 0 的行将被排除,不参与后续的数据转换或结果集返回。
在底层表引擎支持的情况下,WHERE 子句中的过滤条件会利用索引和分区机制进行数据剪枝优化,从而减少实际需要处理的数据量。
WHERE [columnExpr]
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.whereClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE = MergeTree() ORDER BY id; -- 3. 插入数据 INSERT INTO sample_db.whereClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 4. 执行 where 查询 SELECT * FROM sample_db.whereClause WHERE val>10; -- (1, 11), (1, 12), (2, 20), (2, 21) expected -- 输出结果示例 ┌─id─┬─val─┐ │ 1 │ 11 │ │ 1 │ 12 │ │ 2 │ 20 │ │ 2 │ 21 │ └────┴─────┘
如需判断值是否为 NULL,请使用 IS NULL 和 IS NOT NULL 运算符或 isNull 和 isNotNull 函数。因为包含 NULL 的普通条件表达式通常不会返回预期结果。
示例
-- 1. 创建测试表 t_null CREATE TABLE sample_db.t_null ON CLUSTER sample_cluster ( x Int8, y Nullable(Int8) ) ENGINE=MergeTree() ORDER BY x; -- 2. 插入数据 INSERT INTO sample_db.t_null VALUES (1, NULL), (2, 3); -- 3. 查询 y 字段为 NULL 的记录 SELECT * FROM sample_db.t_null WHERE y IS NULL; -- 输出结果示例 ┌─x─┬────y─┐ │ 1 │ ᴺᵁᴸᴸ │ └───┴──────┘ -- 4. 查询 y 字段不为 0 的记录 SELECT * FROM sample_db.t_null WHERE y != 0; -- 输出结果示例 ┌─x─┬─y─┐ │ 2 │ 3 │ └───┴───┘
GROUP BY 子句用于将 SELECT 查询结果进行分组聚合处理。以 [columnExprList] 作为分组键时,聚合结果的行数与源表中分组键的唯一值数量一致,每个唯一分组键对应一行聚合结果。
GROUP BY [(][columnExprList][)] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
NULL processing:NULL 处理,ByteHouse 将 NULL 是为有效分组值。如果分组键包含 NULL 值,它将在结果中显示。WITH ROLLUP:根据 GROUP BY 列表中键表达式的顺序生成小计。
WITH CUBE:计算 GROUP BY 列表中键表达式每种组合的小计。WITH TOTALS:计算 GROUP BY 列表中所有键表达式组合的小计。-- 1. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.groupByClause ON CLUSTER sample_cluster ( x UInt32, y Nullable(UInt32) ) ENGINE=MergeTree ORDER BY x; -- 2. 插入测试数据 INSERT INTO sample_db.groupByClause VALUES (1,2), (2,NULL), (3,2), (3,3), (3,NULL); -- 3. 执行查询 SELECT sum(x), y FROM sample_db.groupByClause GROUP BY y; -- 输出结果示例 ┌─sum(x)─┬────y─┐ │ 5 │ ᴺᵁᴸᴸ │ │ 3 │ 3 │ │ 4 │ 2 │ └────────┴──────┘
-- 1. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.groupByClause1 ON CLUSTER sample_cluster ( year UInt32, month UInt32, day UInt32 ) ENGINE=MergeTree ORDER BY year; -- 2. 插入测试数据 INSERT INTO sample_db.groupByClause1 VALUES (2019,1,5), (2019,1,15), (2020,1,5), (2020,1,15), (2021,1,5), (2021,1,15); -- 3. 查询数据 SELECT year, month, day, count() FROM sample_db.groupByClause1 GROUP BY year, month, day WITH ROLLUP;
由于 GROUP BY 部分包含三个键表达式,结果包含从右到左汇总的小计表:
返回结果:
┌─year─┬─month─┬─day─┬─count()─┐ │ 2020 │ 1 │ 5 │ 1 │ │ 2019 │ 1 │ 5 │ 1 │ │ 2021 │ 1 │ 15 │ 1 │ │ 2019 │ 1 │ 15 │ 1 │ │ 2020 │ 1 │ 15 │ 1 │ │ 2021 │ 1 │ 5 │ 1 │ │ 2020 │ 1 │ 0 │ 2 │ │ 2019 │ 1 │ 0 │ 2 │ │ 2021 │ 1 │ 0 │ 2 │ │ 2019 │ 0 │ 0 │ 2 │ │ 2021 │ 0 │ 0 │ 2 │ │ 2020 │ 0 │ 0 │ 2 │ │ 0 │ 0 │ 0 │ 6 │ └──────┴───────┴─────┴─────────┘
-- 1. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.groupByClause2 ON CLUSTER sample_cluster ( year UInt32, month UInt32, day UInt32 ) ENGINE=MergeTree ORDER BY year; -- 2. 插入测试数据 INSERT INTO sample_db.groupByClause2 VALUES (2019,1,5), (2019,1,15), (2020,1,5), (2020,1,15), (2021,1,5), (2021,1,15); -- 3. 查询数据 SELECT year, month, day, count() FROM sample_db.groupByClause2 GROUP BY year, month, day WITH CUBE;
由于 GROUP BY 部分包含三个键表达式,结果包含八个小计表,涵盖所有键表达式组合的小计:
返回结果:
┌─year─┬─month─┬─day─┬─count()─┐ │ 2020 │ 1 │ 5 │ 1 │ │ 2019 │ 1 │ 5 │ 1 │ │ 2021 │ 1 │ 15 │ 1 │ │ 2019 │ 1 │ 15 │ 1 │ │ 2020 │ 1 │ 15 │ 1 │ │ 2021 │ 1 │ 5 │ 1 │ │ 2020 │ 1 │ 0 │ 2 │ │ 2019 │ 1 │ 0 │ 2 │ │ 2021 │ 1 │ 0 │ 2 │ │ 2020 │ 0 │ 15 │ 1 │ │ 2019 │ 0 │ 5 │ 1 │ │ 2019 │ 0 │ 15 │ 1 │ │ 2020 │ 0 │ 5 │ 1 │ │ 2021 │ 0 │ 5 │ 1 │ │ 2021 │ 0 │ 15 │ 1 │ │ 2019 │ 0 │ 0 │ 2 │ │ 2021 │ 0 │ 0 │ 2 │ │ 2020 │ 0 │ 0 │ 2 │ │ 0 │ 1 │ 5 │ 3 │ │ 0 │ 1 │ 15 │ 3 │ │ 0 │ 1 │ 0 │ 6 │ │ 0 │ 0 │ 5 │ 3 │ │ 0 │ 0 │ 15 │ 3 │ │ 0 │ 0 │ 0 │ 6 │ └──────┴───────┴─────┴─────────┘
-- 1. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.groupByClause3 ON CLUSTER sample_cluster ( year UInt32, month UInt32, day UInt32 ) ENGINE=MergeTree ORDER BY year; -- 2. 插入测试数据 INSERT INTO sample_db.groupByClause3 VALUES (2019,1,5), (2019,1,15), (2020,1,5), (2020,1,15), (2021,1,5), (2021,1,15); -- 3. 查询数据 SELECT year, month, day, count() FROM sample_db.groupByClause3 GROUP BY year, month, day WITH TOTALS;
如果指定了 WITH TOTALS 修饰符,将会计算另一行。
返回结果:
┌─year─┬─month─┬─day─┬─count()─┐ │ 2020 │ 1 │ 5 │ 1 │ │ 2019 │ 1 │ 5 │ 1 │ │ 2021 │ 1 │ 15 │ 1 │ │ 2019 │ 1 │ 15 │ 1 │ │ 2020 │ 1 │ 15 │ 1 │ │ 2021 │ 1 │ 5 │ 1 │ └──────┴───────┴─────┴─────────┘ Totals: ┌─year─┬─month─┬─day─┬─count()─┐ │ 0 │ 0 │ 0 │ 6 │ └──────┴───────┴─────┴─────────┘
为列表达式的每个不同值选择前 m 行结果。
LIMIT m BY [columnExprList]
在此示例中,对于每个 id(包括值 1 和 2),我们需要返回 2 行。
-- 1. 创建测试表 CREATE TABLE sample_db.limitByClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE = MergeTree() ORDER BY id; -- 2. 插入数据 INSERT INTO sample_db.limitByClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 3. 为每个 id 返回前 2 行 SELECT * FROM sample_db.limitByClause ORDER BY id, val LIMIT 2 BY id; -- 输出结果 ┌─id─┬─val─┐ │ 1 │ 10 │ │ 1 │ 11 │ │ 2 │ 20 │ │ 2 │ 21 │ └────┴─────┘
过滤由 groupBy 子句生成的聚合结果,类似于 WHERE 子句,但区别在于 WHERE 子句在聚合之前执行,而 HAVING 子句在聚合之后执行。
HAVING [columnExpr](http://columnexpr/)
-- 1. 创建测试表 CREATE TABLE sample_db.havingClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE = MergeTree() ORDER BY id; -- 2. 插入数据 INSERT INTO sample_db.havingClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 3. 过滤结果 SELECT id FROM sample_db.havingClause GROUP BY id HAVING count(id)>2; -- 输出结果 ┌─id─┐ │ 1 │ └────┘
SELECT DISTINCT 用于从查询结果中过滤重复行,确保结果集中所有列值完全相同的行仅保留一行。
空处理
在 DISTINCT 逻辑中,NULL 被视为一个特定值,且 NULL == NULL 成立。这意味着:
NULL 值会被视为重复值,仅保留一行;NULL 的逻辑(如 NULL = NULL 返回 UNKNOWN)不同。替代办法
通过 GROUP BY 指定所有 SELECT 列(不使用聚合函数)可实现与 DISTINCT 子句相同的去重效果。二者的差异在于:
DISTINCT 可以与 GROUP BY 一起使用。ORDER BY 并且指定 LIMIT 时,系统读取到所需数量的唯一行后立即终止。注意
若 SELECT 语句包含数组类型的列,DISTINCT 子句不支持直接去重。
ByteHouse 支持在同一个查询中使用 DISTINCT 和 ORDER BY 查询不同的列,DISTINCT 子句先于 ORDER BY 子句执行,即去重后再排序。
示例基于下表执行两次查询,演示执行顺序逻辑。
┌─a─┬─b─┐ │ 2 │ 1 │ │ 1 │ 2 │ │ 3 │ 3 │ │ 2 │ 4 │ └─┴───┘
查询 1:
执行 SELECT DISTINCT a FROM t1 ORDER BY b ASC 查询数据,其逻辑为:先对 a 去重(得到 {1,2,3}),再按 b 升序排序结果行。
-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample_db.distinctClause ON CLUSTER sample_cluster ( a UInt32, b UInt32 ) ENGINE = MergeTree ORDER BY a; -- 2. 插入测试数据 INSERT INTO sample_db.distinctClause VALUES (2, 1), (1, 2), (3, 3), (2, 4); -- 3. 查询 SELECT DISTINCT a FROM sample_db.distinctClause ORDER BY b ASC
返回结果如下:
┌─a─┐ │ 2 │ │ 1 │ │ 3 │ └───┘
查询 2:
改变排序方向 SELECT DISTINCT a FROM t1 ORDER BY b DESC 再次执行查询,其逻辑为:去重后按 b 降序排序。
-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample_db.distinctClause ON CLUSTER sample_cluster ( a UInt32, b UInt32 ) ENGINE = MergeTree ORDER BY a; -- 2. 插入测试数据 INSERT INTO sample_db.distinctClause VALUES (2, 1), (1, 2), (3, 3), (2, 4); -- 3. 查询 SELECT DISTINCT a FROM sample_db.distinctClause ORDER BY b DESC
返回结果如下:
┌─a─┐ │ 3 │ │ 1 │ │ 2 │ └───┘
从返回结果中可知,DISTINCT 先完成去重,再按 ORDER BY 排序,因此排序不影响去重结果,但影响最终输出顺序。
LIMIT 子句用于限制查询结果返回的行数。
LIMIT m -- 返回前 m 行 LIMIT n, m -- 跳过前 n 行,返回后续 m 行 LIMIT m OFFSET n -- 等价于 LIMIT n, m LIMIT ... WITH TIES -- 返回所有并列行
LIMIT m:返回起始的 m 行。LIMIT n, m 或 LIMIT m OFFSET n:跳过前 n 行后返回 m 行。n 和 m 必须是非负整数。WITH TIES 扩展:当最后一行存在排序值并列时,返回所有并列行。-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample_db.limitClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree() ORDER BY id; -- 2. 插入测试数据 INSERT INTO sample_db.limitClause VALUES (1), (2), (3), (4), (5); -- 3. 查询前两行数据 SELECT * FROM sample_db.limitClause LIMIT 2; -- 输出结果 ┌─id─┐ │ 1 │ │ 2 │ └────┘
-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample.limitClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree() OEDER BY id; -- 2. 插入测试数据 INSERT INTO sample.limitClause VALUES (1), (2), (3), (4), (5); -- 3. 查询时跳过前 n 行,返回后续 m 行 SELECT * FROM sample_db.limitClause LIMIT 1,2; --- 查询方式不同,但查询效果相同 SELECT * FROM sample_db.limitClause LIMIT 2 OFFSET 1; -- 输出结果 ┌─id─┐ │ 2 │ │ 3 │ └────┘
-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample.limitClause ON CLUSTER sample_cluster ( id UInt32 ) ENGINE=MergeTree() OEDER BY id; -- 2. 插入测试数据 INSERT INTO sample.limitClause VALUES (1), (2), (3), (4), (5); -- 3. 返回所有并列行 SELECT * FROM ( SELECT number%50 AS n FROM numbers(100) ) ORDER BY n LIMIT 0,5 WITH TIES -- 输出结果 ┌─n─┐ │ 0 │ │ 0 │ │ 1 │ │ 1 │ │ 2 │ │ 2 │ └───┘
UNION ALL 用于合并多个 SELECT 语句的结果集,将各查询结果按行拼接后返回。
UNION ALL ...
-- 1. 创建测试表 CREATE TABLE IF NOT EXISTS sample_db.unionClause1 ON CLUSTER sample_cluster ( CounterID UInt32, c UInt64 ) ENGINE = MergeTree ORDER BY CounterID; CREATE TABLE IF NOT EXISTS sample_db.unionClause2 ON CLUSTER sample_cluster ( CounterID UInt32, c UInt64 ) ENGINE = MergeTree ORDER BY CounterID; -- 2. 插入测试数据 INSERT INTO sample_db.unionClause1 VALUES (1, 100), (2, 200); INSERT INTO sample_db.unionClause2 VALUES (1, 300), (3, 400); -- 3. 合并查询结果 SELECT CounterID, 1 AS source, c FROM sample_db.unionClause1 UNION ALL SELECT CounterID, 2 AS source, c FROM sample_db.unionClause2; -- 输出结果 ┌─CounterID─┬─source─┬───c─┐ │ 1 │ 2 │ 300 │ │ 3 │ 2 │ 400 │ │ 1 │ 1 │ 100 │ │ 2 │ 1 │ 200 │ └───────────┴────────┴─────┘
结果集处理规则:
Nullable 与兼容的非Nullable类型,统一提升为 Nullable。ORDER BY/LIMIT 仅作用于单个查询(非最终结果)。需处理最终结果时,将整个 UNION ALL 置于子查询中,示例如下:
SELECT * FROM ( ... UNION ALL ... ) ORDER BY col
ByteHouse 仅支持 UNION ALL,不支持 UNION DISTINCT。如需使用 UNION DISTINCT,您可以使用 SELECT DISTINCT 子查询中包含 UNION ALL,示例如下:
SELECT DISTINCT * FROM ( ... UNION ALL ... )
使用 SETTINGS 子句配置数据库参数,覆盖默认设置。
SETTINGS [settingExprList]
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE sample_db.fromClause ON CLUSTER sample_cluster ( id UInt32, val UInt32 ) ENGINE=MergeTree() ORDER BY id; -- 3. 插入数据 INSERT INTO sample_db.fromClause VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21); -- 4. 查询数据并调整并行度 SELECT * FROM sample_db.fromClause SETTINGS max_threads = 4;
使用 INTO OUTFILE 子句将查询结果直接输出到客户端本地文件。
此功能仅支持通过命令行客户端 和 clickhouse-local 工具中使用,因此通过 HTTP 接口发送查询将会失败。如果目标文件已经存在,则查询将失败。
默认值输出格式是 TabSeparated (与命令行批处理模式一致)。
INTO OUTFILE filename
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.col_names ON CLUSTER sample_cluster ( aa Int8, ab Int8, bc Int8 ) ENGINE = MergeTree ORDER BY aa; -- 3. 插入测试数据 INSERT INTO sample_db.col_names VALUES (1, 2, 3),(4, 5, 6); -- 4. 导出数据到本地文件,使用时需替换为实际存储路径 SELECT * FROM sample_db.col_names INTO OUTFILE '~/sampel_outfile.txt'
ByteHouse 支持序列化格式,可用于查询结果。 有多种方法可以选择格式化 SELECT 的输出,其中之一是指定 FORMAT format 在查询结束时以任何特定格式获取结果集。
FORMAT format
-- 1. 创建需要的库 CREATE DATABASE IF NOT EXISTS sample_db ON CLUSTER sample_cluster; -- 2. 创建示例表 CREATE TABLE IF NOT EXISTS sample_db.col_names ON CLUSTER sample_cluster ( aa Int8, ab Int8, bc Int8 ) ENGINE = MergeTree ORDER BY aa; -- 3. 插入测试数据 INSERT INTO sample_db.col_names VALUES (1, 2, 3),(4, 5, 6); -- 4. 查询数据并制定输出格式 SELECT * FROM sample_db.col_names FORMAT CSVWithNames; -- 输出结果 aa ab bc 1 2 3 4 5 6