跳转到主内容
跳转到主内容

LIMIT BY 子句

带有 LIMIT n BY expressions 子句的查询会为 expressions 的每个不同取值组合选取前 n 行。LIMIT BY 的键可以包含任意数量的表达式

ClickHouse 支持以下语法形式:

  • LIMIT [offset_value, ]n BY expressions
  • LIMIT n OFFSET offset_value BY expressions

在查询处理过程中,ClickHouse 会按排序键顺序选择数据。可以通过 ORDER BY 子句显式设置排序键,或者由表引擎隐式确定 (只有在使用 ORDER BY 时才可以保证行顺序;否则由于多线程执行,数据块中的行将不会有序) 。然后 ClickHouse 应用 LIMIT n BY expressions,并为 expressions 的每个不同组合返回前 n 行。如果指定了 OFFSET,则对于属于某个 expressions 组合的每个数据块,ClickHouse 会从块开头跳过 offset_value 行,并最多返回 n 行作为结果。如果 offset_value 大于数据块中的行数,ClickHouse 会从该块返回 0 行。

注意

LIMIT BYLIMIT 无关。它们可以在同一个查询中同时使用。

如果希望在 LIMIT BY 子句中使用列编号而不是列名,请启用 enable_positional_arguments 设置。

示例

示例表格:

CREATE TABLE limit_by(id Int, val Int) ENGINE = Memory;
INSERT INTO limit_by VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21);

查询:

SELECT * FROM limit_by ORDER BY id, val LIMIT 2 BY id;
┌─id─┬─val─┐
│  1 │  10 │
│  1 │  11 │
│  2 │  20 │
│  2 │  21 │
└────┴─────┘
SELECT * FROM limit_by ORDER BY id, val LIMIT 1, 2 BY id;
┌─id─┬─val─┐
│  1 │  11 │
│  1 │  12 │
│  2 │  21 │
└────┴─────┘

SELECT * FROM limit_by ORDER BY id, val LIMIT 2, 1 BY id 查询返回相同的结果。

下面的查询会为每个 domain, device_type 组合返回前 5 个 referrer,总行数最多为 100 行 (LIMIT n BY + LIMIT) 。

SELECT
    domainWithoutWWW(URL) AS domain,
    domainWithoutWWW(REFERRER_URL) AS referrer,
    device_type,
    count() cnt
FROM hits
GROUP BY domain, referrer, device_type
ORDER BY cnt DESC
LIMIT 5 BY domain, device_type
LIMIT 100;

LIMIT BY 也支持负的 limit 和 offset。与负 LIMIT 子句类似,您可以在 LIMIT BY 中使用负值,以从每个分组的末尾选取行。

SELECT * FROM limit_by ORDER BY id, val LIMIT -2 BY id;
┌─id─┬─val─┐
│  1 │  11 │
│  1 │  12 │
│  2 │  20 │
│  2 │  21 │
└────┴─────┘

返回每个 id 的最后 2 行。对于 id = 1,返回第 11 行和第 12 行;对于 id = 2,由于该组只有 2 行,因此两行都会返回。

SELECT * FROM limit_by ORDER BY id, val LIMIT -1 OFFSET -1 BY id;
┌─id─┬─val─┐
│  1 │  11 │
│  2 │  20 │
└────┴─────┘

返回每个 id 的倒数第二行:末尾的 OFFSET -1 会去掉每组的最后一行,而前面的 -1 则保留剩余部分中的最后一行。

也可以混合使用符号不同的 LIMITOFFSET。例如,要去掉每组的第一行,然后保留剩余部分中的最后 2 行:

SELECT * FROM limit_by ORDER BY id, val LIMIT -2 OFFSET 1 BY id;
┌─id─┬─val─┐
│  1 │  11 │
│  1 │  12 │
│  2 │  21 │
└────┴─────┘

对于 id = 1,第一行 (10) 会被跳过;11, 12 中最后 2 行都会返回。对于 id = 2,第一行 (20) 会被跳过,因此只剩下 21

LIMIT BY ALL

LIMIT BY ALL 等价于列出 SELECT 子句中所有不是聚合函数的表达式。

例如:

SELECT col1, col2, col3 FROM table LIMIT 2 BY ALL;

与……相同

SELECT col1, col2, col3 FROM table LIMIT 2 BY col1, col2, col3;

对于一种特殊情况,如果存在一个函数,其参数同时包含聚合函数和其他字段,则 LIMIT BY 键将包含可从该函数中提取的最多数量的非聚合字段。

例如:

SELECT substring(a, 4, 2), substring(substring(a, 1, 2), 1, count(b)) FROM t LIMIT 2 BY ALL;

与……相同

SELECT substring(a, 4, 2), substring(substring(a, 1, 2), 1, count(b)) FROM t LIMIT 2 BY substring(a, 4, 2), substring(a, 1, 2);

示例

示例表:

CREATE TABLE limit_by(id Int, val Int) ENGINE = Memory;
INSERT INTO limit_by VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21);

查询:

SELECT * FROM limit_by ORDER BY id, val LIMIT 2 BY id;
┌─id─┬─val─┐
│  1 │  10 │
│  1 │  11 │
│  2 │  20 │
│  2 │  21 │
└────┴─────┘
SELECT * FROM limit_by ORDER BY id, val LIMIT 1, 2 BY id;
┌─id─┬─val─┐
│  1 │  11 │
│  1 │  12 │
│  2 │  21 │
└────┴─────┘

SELECT * FROM limit_by ORDER BY id, val LIMIT 2 OFFSET 1 BY id 查询返回相同的结果。

使用 LIMIT BY ALL

SELECT id, val FROM limit_by ORDER BY id, val LIMIT 2 BY ALL;

这相当于:

SELECT id, val FROM limit_by ORDER BY id, val LIMIT 2 BY id, val;