2013-02-18 57 views
5

如果我有以下的玩具查询Postgresql plpgsql/sql支持where子句中的短路吗?

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中存在的所有记录进行迭代,然后在FOR LOOP的范围内使用some_slow_func( )。但我很好奇,如果SQL支持,或plpgsql支持短路。

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

http://www.postgresql.org/message-id/[email protected]

但其中一个答复说,为了能够通过强制执行子查询。我不确定他在说什么。我知道子选票是什么,但我不知道如何执行命令?有人能为我澄清这一点吗?

+0

我不认为短路是相关的; SQL应该是面向集合的,其结果不应该依赖于评估的顺序。对这个*的一个限制可能是两个子查询的联合,这两个子查询都带有一个LIMIT,并在整个查询中增加一个额外的LIMIT。但LIMIT仍然是边界线...评估的副作用不应该在真正的关系型RSBMS中实现(可能除了LATERAL)。总之:评估顺序只影响性能,而不是结果的正确性,恕我直言。这就是为什么我们应该把评估顺序留给规划者。 – wildplasser 2013-02-18 22:14:17

回答

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任意单位表示。

+0

是的,使用WITH将是一个很好的快速限制设置的方法。不知道为什么我没有想到这一点....我喜欢用WITH。另外,甚至没有考虑改变计划者的成本。我知道它,但我一直让postgres照顾优化。我以前从来没有用过它,但会更多地阅读它。感谢您引起我的注意! – enigmasck 2013-02-19 13:17:35

+0

当然,您定义函数时还可以包含'COST N'。 – jpmc26 2014-11-04 23:15:16

0

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

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

+0

感谢您的参考。我一整天都在看postgresql文档,并没有遇到这种解释。感谢您的快速回复。 – enigmasck 2013-02-18 19:52:04

+0

没问题。一个提示 - 当你在看这些论坛时,底部通常会有“回复”和“回复”部分。如果您在响应下关注链接,您可能会获得更多信息。在这种情况下,我所做的只是按照汤姆莱恩在OP中链接的帖子上的回复。 – 2013-02-18 19:54:59

2

我知道这是一个老问题,但最近碰到类似的问题,并发现在WHERE子句中使用CASE谓词对我更好。在上面的答案中:

SELECT * 
    FROM my_tables 
WHERE CASE WHEN my_id in (SELECT my_other_id 
          FROM my_other_tables) 
      AND some_slow_func(arg) BETWEEN 1 AND 2 
      THEN 1 
      ELSE 0 
     END = 1; 

这使得SQL稍微多于DB不可知。当然,如果您在my_id上有一些索引,它可能不会使用索引,但根据您所处的上下文,这可能是一个不错的选择。