Postgresql plpgsql / sql是否支持where子句中的短路?

时间:2013-02-18 19:00:22

标签: sql postgresql plpgsql

如果我有以下玩具查询

SELECT *
FROM my_tables
WHERE my_id in (
    SELECT my_other_id
    FROM my_other_tables
) AND some_slow_func(arg) BETWEEN 1 AND 2;

WHERE子句中的第一个条件是否会短路第二个具有复杂运行时间的条件?

我正在研究一些实际上是plpgsql中FOR LOOP的一部分的sql,我可以对my_other_tables中存在的所有记录进行迭代,然后使用some_slow_func()在FOR LOOP的范围内进行测试。但我很好奇是否支持sql,或者plpgsql支持短路。

一些研究: 我查看了Postgres的邮件列表,发现这说SQL一般不支持短路:

http://www.postgresql.org/message-id/171423D4-9229-4D56-B06B-58D29BB50A77@yahoo.com

但其中一个回复表明订单可以通过子选择来强制执行。我不确定他在说什么。我知道子选择是什么,但我不确定如何强制执行命令?有人可以为我澄清一下吗?

3 个答案:

答案 0 :(得分:6)

如文档所述,WHERE子句中的评估顺序应该是不可预测的。

与子查询不同。对于当前版本,驱动评估顺序的最简单和常用技术是在CTE中编写子查询。为了确保首先评估IN(...),您的代码可以写成:

WITH subquery AS
(select * from my_tables
  WHERE my_id in (SELECT my_other_id FROM my_other_tables)
)
SELECT * FROM subquery
  WHERE some_slow_func(arg) BETWEEN 1 AND 2;

您可能调整的其他内容是函数向优化器发出信号表明它很慢的成本。函数的默认开销是100,可以使用如下语句更改它:

ALTER FUNCTION funcname(argument types) cost N;

其中N是估算的每次通话费用,以任意单位表示,应与Planner Cost Constants进行比较。

答案 1 :(得分:2)

我知道这是一个老问题,但最近遇到了类似的问题,发现在WHERE子句中使用CASE谓词对我来说效果更好。在上面的答案的背景下:

Applicative

这使得SQL与DB无关。当然,如果你在my_id上有一些索引,它可能不会使用索引,但根据你所处的上下文,这可能是一个不错的选择。

答案 2 :(得分:0)

根据the Postgresql docsthis answer by Tom Lane,WHERE约束的执行顺序不可靠。

我认为你最好的选择可能是将WHERE子句的其他部分添加到函数的顶部并“快速失败”;即,在您的函数中运行my_id in ( SELECT my_other_id FROM my_other_tables),如果它没有通过,请在进行密集处理之前返回。那会让你产生同样的效果。