You need to enable JavaScript to run this app.
文档中心
ByteHouse 企业版

ByteHouse 企业版

复制全文
下载 pdf
DQL
查询语句 (SELECT)
复制全文
下载 pdf
查询语句 (SELECT)

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 子句

语法

SELECT [DISTINCT] expr_list

参数说明

参数

说明

DISTINCT

可选参数,用于去重查询结果中的重复行。在处理 NULL 值时,会将所有 NULL 视为相同的值。

*(星号)

可在查询的任意位置替代表达式,执行查询时会自动扩展为表中所有列的列表(不包含 MATERIALIZED 和 ALIAS 列)。

AS 别名

可为查询结果中的列指定自定义名称,增强结果可读性。

使用场景

  • 在结果中包含所有列,请使用星号 (*)符号。 例如,

    SELECT * FROM ...
    
  • 将结果中包含 'a' 的列,可以使用 COLUMNS 表达。

    SELECT COLUMNS('a') FROM table_name;
    

示例 1

查询表中所有列名包含 '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 │
   └────┴────┘

示例 2

使用多个 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 进行强过滤时。
    • 在子查询中使用,因为外部查询不需要的列从子查询中排除。
    • 其他场景不建议使用,可能影响性能。

WITH 子句

Bytehouse 支持公用表表达式(Common table expression,CTE)。WITH 子句的结果可以在后续的 SELECT 查询中使用。但该功能存在一定的限制,包括:

  • CTE 中不允许递归;
  • CTE 中不允许子查询。

语法

WITH [columnExpr] AS identifier

参数说明

参数

说明

columnExpr

定义 CTE 的表达式(通常是子查询),生成临时结果集。

identifier

CTE 的名称(别名),用于在后续 SELECT 中引用此结果集。

示例 1:使用常量表达式作为 “variable”

WITH '2024-07-01 15:23:00' as ts_upper_bound
SELECT *
FROM system.tables
WHERE 
metadata_modification_time <= ts_upper_bound 
LIMIT 5;

示例 2:从 SELECT 子句列表中逐出 sum(bytes)表达式结果

WITH sum(total_bytes) as s
SELECT
    formatReadableSize(s),
    database
FROM system.tables
GROUP BY database
ORDER BY s 
LIMIT 3;

示例 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 

示例 4:在子查询中重用表达式

WITH ['hello'] AS hello
SELECT
    hello,
    *
FROM
(
    WITH ['hello'] AS hello
    SELECT hello
)

-- 结果

┌─hello─────┬─hello_1─────┐
│ ['hello'] │ ['hello'] │
└───────────┴───────────┘

FROM 子句

FROM 子句指定了读取数据的来源,支持设置为表、子查询、表函数。

语法

  • 从表中读取数据

    FROM [tableIdentifier] [FINAL] [sampleClause] 
    
  • 从子查询中读取数据

    FROM ([selectUnionStmt]) [FINAL] [sampleClause]
    
  • 从表函数中读取数据

    FROM tableFunctionExpr [FINAL] [sampleClause]
    

参数说明

参数

说明

tableIdentifier

指定表或视图的名称。

selectUnionStmt

子查询语句,需使用括号包裹。

tableFunctionExpr

表函数表达式。

FINAL

当指定 FINAL 时,ByteHouse 在返回结果之前会完全合并数据,因此执行在合并过程中发生的所有数据转换。

sampleClause

数据采样表达式(如 SAMPLE 0.1 表示 10% 数据采样)。

示例 1:​从表中查询

-- 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 │
   └────┴─────┘

示例 2:从子查询中查询

-- 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 │
   └────┴─────┘

示例 3:从表函数中查询

这里使用表函数 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 │
    └────────┘

SAMPLE 子句

前提条件

如果要使用采样,必须在创建 MergeTree 表时声明采样表达式,详见 SAMPLE BY 语句。

功能概述

Sample 子句允许进行近似的 SELECT 查询处理。启用 sample 子句后,查询不会在所有数据上执行,而只会在某个数据片段上执行,适用于以下场景:

  • 有严格的时间要求(如小于 100 毫秒),但硬件资源扩容成本不可行时。
  • 原始数据不准确,因此近似结果不会显著降低质量。
  • 业务需求允许接受近似结果(如成本优化场景),或仅为高级用户提供精确结果。

以下为数据采样的功能:

  • 数据采样是一种确定性机制。 相同 SELECT .. SAMPLE 查询始终返回相同结果。
  • 若不同表使用相同的采样键(如用户 ID),相同系数的采样会选择相同的数据子集。这也意味着您可以在子查询中使用 IN、JOIN 操作。
  • 采样允许从磁盘读取更少的数据。 请注意,您必须正确指定采样键。

SAMPLE 子句支持以下语法:

SAMPLE 子句语法

描述

SAMPLE k

按比例采样,k 表示抽取数据的比例,设置为从 0 到 1 的数字,支持小数和小数表示法。 例如,SAMPLE 0.1 表示查询 10% 的数据。

SAMPLE n

按行数采样,n 表示最小采样行数,设置为足够大的整数。
该查询是在至少一个样本上执行的 n 行(但不超过这个)。 例如,SAMPLE 10000000 表示在至少 10,000,000 行上运行查询。

SAMPLE k OFFSET m

带偏移的比例采样,k 为采样比例,m 为偏移比例,均设置为从 0 到 1 的数字。例如,SAMPLE 0.2 OFFSET 0.3,表示从 30% 位置开始,取 20% 的数据区间。

SAMPLE K

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% 数据

SAMPLE N

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; 

SAMPLE K OFFSET M

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 子句

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

  • 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 子句

对于包含数组列的表,数组连接(array join)可以生成一个新表,该新表的列包含初始列中每个单独的数组元素,同时重复复制其他列的值。

语法

每个 SELECT 查询中只能指定一个 arrayJoin 子句。

[LEFT] ARRAY JOIN [columnExprList] 

参数说明

参数

说明

ARRAY JOIN

仅展开非空数组,空数组所在行不会在结果中展示。

LEFT ARRAY JOIN

JOIN 的结果包含具有空数组的行。空数组展开为数组元素类型的默认值(通常为 0、空字符串或 NULL)。

示例

-- 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 子句是一种优化机制,用于更高效地应用过滤。即使未显式指定 PREWHERE 子句,系统也将自动将部分 WHERE 条件移动到 prewhere 阶段执行。PREWHERE 子句的作用仅在于当你认为自己比默认优化做得更好时,控制这种优化。
使用 prewhere 优化时,具体流程如下:

  1. 首先只读取执行 prewhere 表达式所需的列。
  2. 然后读取执行查询其余部分所需的其他列,但仅读取那些在某些行中 prewhere 表达式为 “true” 的数据块。
    若存在大量数据块,其所有行中 prewhere 表达式均为 “false”,且 prewhere 所需列数少于查询其他部分的列数,则这种优化能可显著降低磁盘 IO 开销。

语法

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 子句

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 子句

GROUP BY 子句用于将 SELECT 查询结果进行分组聚合处理。以 [columnExprList] 作为分组键时,聚合结果的行数与源表中分组键的唯一值数量一致,每个唯一分组键对应一行聚合结果。

语法

GROUP BY [(][columnExprList][)] [WITH ROLLUP|WITH CUBE] [WITH TOTALS] 
  • NULL processing:NULL 处理,ByteHouse 将 NULL 是为有效分组值。如果分组键包含 NULL 值,它将在结果中显示。
  • WITH ROLLUP:根据 GROUP BY 列表中键表达式的顺序生成小计。
    • 小计行会添加到结果表之后。
    • 在小计行中,已“分组”的键表达式的值将设置为 0 或空行。
  • WITH CUBE:计算 GROUP BY 列表中键表达式每种组合的小计。
  • WITH TOTALS:计算 GROUP BY 列表中所有键表达式组合的小计。

示例 1:Group by 键 包含 NULL 值

-- 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 │
   └────────┴──────┘

示例 2:按组汇总的 WITH ROLLUP 修饰符

-- 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 部分包含三个键表达式,结果包含从右到左汇总的小计表:

  • GROUP BY year, month, day;
  • GROUP BY year, month(并且 day 列填充为零);
  • GROUP BY year(现在 month 和 day 列都填充为零);
  • 总计(所有三个键表达式列都为零)。

返回结果:

┌─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 │
    └──────┴───────┴─────┴─────────┘

示例 3:按组汇总的 WITH CUBE 修饰符

-- 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 部分包含三个键表达式,结果包含八个小计表,涵盖所有键表达式组合的小计:

  • GROUP BY year, month, day
  • GROUP BY year, month
  • GROUP BY year, day
  • GROUP BY year
  • GROUP BY month, day
  • GROUP BY month
  • GROUP BY day
  • 总计(所有三个键表达式列都为零)

返回结果:

┌─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 │
    └──────┴───────┴─────┴─────────┘

示例 4:按组汇总的 WITH TOTAL 修饰符

-- 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 修饰符,将会计算另一行。

  • GROUP BY year, month, day
  • 总计(所有三个键表达式列都为零)

返回结果:

┌─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 │
   └──────┴───────┴─────┴─────────┘

LIMIT BY 子句

为列表达式的每个不同值选择前 m 行结果。

语法

LIMIT m BY [columnExprList]

示例:按列表达式限制 m 行

在此示例中,对于每个 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 │
   └────┴─────┘

HAVING 子句

过滤由 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 │
   └────┘

DISTINCT 子句

SELECT DISTINCT 用于从查询结果中过滤重复行,确保结果集中所有列值完全相同的行仅保留一行。
空处理
在 DISTINCT 逻辑中,NULL 被视为一个特定值,且 NULL == NULL 成立。这意味着:

  • 不同行中相同位置的 NULL 值会被视为重复值,仅保留一行;
  • 该处理方式与 SQL 中多数场景下 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 子句用于限制查询结果返回的行数。

语法

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 扩展:当最后一行存在排序值并列时,返回所有并列行。
  • 如果没有 ORDER BY 子句显式排序结果,结果的行选择可能是任意的和非确定性的。

示例 1:LIMIT m

-- 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 │
   └────┘

示例 2:LIMIT n, m

-- 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 │
   └────┘

示例 3:LIMIT ... WITH TIES

-- 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 子句

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 │
   └───────────┴────────┴─────┘

结果集处理规则:

  1. 列匹配机制:按列索引顺序匹配各查询的结果列(而非列名),最终结果列名取自第一个查询。
  2. 类型处理:
    • 自动执行类型转换。
    • 若合并字段存在 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 子句配置数据库参数,覆盖默认设置。

语法

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 子句

使用 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' 

FORMAT 子句

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
最近更新时间:2025.08.14 10:17:09
这个页面对您有帮助吗?
有用
有用
无用
无用