DuckDB中的几个操作符表现出非确定性行为。 最显著的是,SQL使用集合语义,这允许结果以不同的顺序返回。 DuckDB利用这一点来提高性能,特别是在执行多线程查询时。 其他因素,如使用不同的编译器、操作系统和硬件架构,也可能导致排序的变化。 本页记录了非确定性是预期行为的情况。 如果您希望使您的查询具有确定性,请参阅“解决非确定性”部分。
集合语义
非确定性的最常见来源之一是SQL使用的集合语义。 例如,如果你重复运行以下查询,你可能会得到两个不同的结果:
SELECT *
FROM (
SELECT 'A' AS x
UNION
SELECT 'B' AS x
);
结果 A
, B
和 B
, A
都是正确的。
不同平台上的不同结果:array_distinct
array_distinct
函数可能在不同的平台上返回不同顺序的结果:
SELECT array_distinct(['A', 'A', 'B', NULL, NULL]) AS arr;
对于这个查询,[A, B]
和 [B, A]
都是有效的结果。
多线程浮点聚合操作
在多线程配置下运行时,浮点数不准确性可能会产生不同的结果:
例如,stddev
和 corr
可能会产生非确定性的结果:
CREATE TABLE tbl AS
SELECT 'ABCDEFG'[floor(random() * 7 + 1)::INT] AS s, 3.7 AS x, i AS y
FROM range(1, 1_000_000) r(i);
SELECT s, stddev(x) AS standard_deviation, corr(x, y) AS correlation FROM tbl
GROUP BY s
ORDER BY s;
此查询的预期标准偏差和相关性对于所有s
值均为0。
然而,当在多个线程上执行时,由于浮点不准确性,查询可能会返回小的数值(0 <= z < 10e-16
)。
处理非确定性
对于大多数使用场景,非确定性不会引起任何问题。 然而,在某些情况下,确定性的结果是可取的。 在这些情况下,尝试以下解决方法:
-
限制线程数量以防止多线程引入的非确定性。
SET threads = 1;
-
强制执行排序。例如,您可以使用
ORDER BY ALL
子句:SELECT * FROM ( SELECT 'A' AS x UNION SELECT 'B' AS x ) ORDER BY ALL;
你也可以使用
list_sort
来对列表进行排序:SELECT list_sort(array_distinct(['A', 'A', 'B', NULL, NULL])) AS i ORDER BY i;
也可以引入确定性洗牌。