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 值。