Skip to main content
如果指定了 SELECT DISTINCT,查询结果中将只保留唯一的行。也就是说,对于结果中所有完全相同的行,每组最终只会保留一行。 你可以指定哪些列的值必须唯一:SELECT DISTINCT ON (column1, column2,...)。如果未指定列,则会将所有列都纳入考虑。 考虑下表:
不指定列时使用 DISTINCT:
在指定列上使用 DISTINCT:

DISTINCT 和 ORDER BY

ClickHouse 支持在同一个查询中,对不同的列分别使用 DISTINCT 和 ORDER BY 子句。DISTINCT 子句会在 ORDER BY 子句之前执行。 考虑下列表:
查询数据:
按不同的排序方向选择数据:
行 2, 4 在排序前被截断。 编写查询时,请将这一实现上的特性考虑在内。

NULL 处理

DISTINCT 对 NULL 的处理方式相当于将 NULL 视为一个特定值,且 NULL==NULL。换句话说,在 DISTINCT 的结果中,包含 NULL 的不同组合都只会出现一次。这与 NULL 在大多数其他场景下的处理方式不同。

替代方案

也可以不使用任何聚合函数,而是对 SELECT 子句中指定的同一组值应用 GROUP BY,从而获得相同的结果。不过,与 GROUP BY 的做法相比,还是有几点不同:
  • DISTINCT 可以与 GROUP BY 一起使用。
  • 在外部执行开始之前,不带 ORDER BY 的查询一旦读到足够数量的不同行以满足 LIMIT,就会停止执行。
  • 在外部执行开始之前且省略 ORDER BY 时,不带 ALL 的 LIMIT ... AFTER ... UNTIL 范围同样可以在该范围结束后让查询停止执行。
  • 在外部执行开始之前,数据块会在处理过程中直接输出。

外部内存中的 DISTINCT

DISTINCT 可以将临时数据写入磁盘,以处理过大、无法全部驻留在内存中的唯一值集合。这会带来额外的磁盘 I/O,并可能导致查询变慢。 有两个设置控制何时开始落盘:
  • max_bytes_before_external_distinct 以字节为单位设置查询总内存的阈值,默认值为 0 (禁用) 。
  • max_bytes_ratio_before_external_distinct 设置 server 或 user 限制下可用内存的占比,该值在执行开始时测得。默认值为 0.5;若两种限制均不适用,则该设置不生效。
当两个阈值同时适用时,取较小者。将这两个设置都设为 0 即可禁用落盘。 max_memory_usage 不会影响该比例。若要相对于查询内存限制来配置落盘,请设置一个低于该限制的绝对阈值。例如,以下查询在 256 MiB 查询内存限制下使用 16 MiB 的落盘阈值:
这些阈值并不会限制内存使用量。请为其他查询处理以及落盘本身预留空间。在内存压力较大时,落盘也可能提前开始。 落盘之前即可返回行,若在此阶段已满足 LIMIT,查询可提前结束。 一旦开始落盘,就必须读完其余输入,才能返回剩下的结果。 如果查询包含 ORDER BY,这些结果将按请求的顺序返回。 当 DISTINCT 的输入已按其键的前缀排序时,不会发生落盘。但前缀相同的行若数量很大,仍可能占用大量内存。 与 optimize_distinct_in_order 一样,落盘可能会将二进制表示不同但比较结果相等的浮点值去重,包括 0.0 和 -0.0,或载荷不同的 NaN 值。
最后修改于 2026年9月26日