14

如果我有以下玩具查询

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

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

4

3 回答 3

11

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

与子查询不同。对于早于版本 12 的 PostgreSQL,驱动评估顺序的最简单和常用的技术是在 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;

从 PostgreSQL 版本 12 开始,WITH优化器可以内联子查询(有关所有详细信息,请参阅WITH 查询的文档页面MATERIALIZED),并且仅在添加子句时才保证非内联:

WITH subquery AS MATERIALIZED
(select * ... the rest is similar as above)

您可能会调整的其他内容是向优化器发出信号缓慢的函数成本。函数的默认成本是100,可以使用如下语句进行更改:

ALTER FUNCTION funcname(argument types) cost N;

其中N是估计的每次呼叫成本,以任意单位表示,应与Planner Cost Constants进行比较。

于 2013-02-18T20:18:48.037 回答
3

我知道这是一个老问题,但最近遇到了类似的问题,发现在 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 与数据库无关。当然,如果您在 my_id 上有一些索引,它可能不会使用索引,但根据您所处的上下文,这可能是一个不错的选择。

于 2016-10-19T16:10:43.447 回答
1

根据Postgresql 文档Tom Lane 的回答,WHERE 约束的执行顺序不可靠。

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

于 2013-02-18T19:25:39.613 回答