我经常使用WHERE子句random() > 0.5
来挑选我的数据的随机子集。现在我注意到,当在子查询中使用set-returns函数时,我得到整个集合或者没有(意味着WHERE random()> 0.5子句被解释为之前集合是被生成)。
e.g:
SELECT num
FROM (
SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num
) AS foo
WHERE random() > 0.5;
这似乎不一致,因为以下查询 将整个集合考虑在内:
SELECT num
FROM (
SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num
) AS foo
WHERE random() > 0.1 * num;
我是否认为这是不一致的还是有意义的?
注意:
无法找到另一个与random()分开测试的功能,但可能还有一些
我也使用generate_series进行了测试
答案 0 :(得分:3)
在第一个查询中,where
子句中的表达式执行一次,因为它与select
列表中的列无关:
Result (cost=0.01..0.51 rows=100 width=0) (actual time=0.017..0.021 rows=10 loops=1)
One-Time Filter: (random() > '0.5'::double precision)
Planning time: 0.156 ms
Execution time: 0.058 ms
在第二种情况下,where
表达式取决于列:
Subquery Scan on foo (cost=0.00..2.76 rows=33 width=4) (actual time=0.052..0.083 rows=5 loops=1)
Filter: (random() > ((0.1 * (foo.num)::numeric))::double precision)
Rows Removed by Filter: 5
-> Result (cost=0.00..0.51 rows=100 width=0) (actual time=0.017..0.022 rows=10 loops=1)
Planning time: 0.119 ms
Execution time: 0.137 ms
答案 1 :(得分:2)
你是对的,这似乎非常不一致。
这里的关键点是random()
是VOLATILE
,(理论上)意味着查询计划程序不应该优化对此函数的任何调用。
有趣的是,只有在使用SELECT f()
调用set-returns函数时才会发生这种情况,而不是SELECT * FROM f()
;此查询给出了预期结果:
SELECT num
FROM (
SELECT * FROM unnest(Array[1,2,3,4,5,6,7,8,9,10]) num
) AS foo
WHERE random() > 0.5;
我不知道这是一个错误还是一个已知的限制,因为有类似的情况需要这种行为。例如,比较以下内容:
SELECT random() FROM generate_series(1,10); -- 10 random numbers
SELECT (SELECT random()) FROM generate_series(1,10); -- 10 copies of the same random number
如果你在这里没有得到明确的答案,你可能想要询问Postgres mailing list你所看到的行为是否有意。
答案 2 :(得分:1)
事实上,postgres邮件列表给出了很好的回复,很可能是一个错误。
这是Tom Lane的答案,包括解决方法:
嗯,我认为这是一个优化错误。有两种合法的行为 这里:SELECT * FROM unnest(ARRAY[1,2,3,4,5,6,7,8,9,10]) WHERE random() > 0.5;
应该(并且确实)重新评估unfst()输出的每一行的WHERE。
SELECT unnest(ARRAY[1,2,3,4,5,6,7,8,9,10]) WHERE random() > 0.5;
应该只评估WHERE一次,因为这会在扩展之前发生 目标列表中的set-returns函数。 (如果您是Oracle用户并且 你认为这个查询有一个隐含的“FROM dual”,WHERE应该 评估来自FROM子句的单行。)
如果你到了这里,给定WHERE在外面的位置 查询,您当然希望对每一行进行评估 内部查询。但是优化器决定它可以推动WHERE 子句下来成为子选择的WHERE。这是合法的 很多情况,但不是在子选择中有SRF的情况 targetlist,因为它推动WHERE在SRF之前发生, 类似于我写的两个查询之间的变化。
我对现有版本中的更改有点犹豫。鉴于缺乏 以前的投诉,似乎更有可能破坏查询 表现如预期,而不是让人开心。但我们可以改变它 在v10及以上,特别是因为其他一些角落的变化 SRF-in-tlist行为正在进行中。
与此同时,您可以通过插入来强制它按照您的意愿工作 在子选择中的通用优化围栏“OFFSET 0”:
=# SELECT num FROM (
SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num OFFSET 0) AS foo WHERE random() > 0.5;
num
-----
1
4
7
9
(4 rows)