如何在SQL(Postgres)中强制过滤评估顺序?

时间:2014-08-05 00:16:24

标签: sql postgresql

我有一张简化的表,看起来大致如下:

id | type | header    | body
===========================================
 1 | A    | {type: A} | {content: "Hi"}
 2 | A    | {type: A} | {content: "Hello"}
 3 | B    | {type: B} | ["Hi","Hello"]

以下查询给出了一个错误:

> select * from Table where header->>'type'='A' and body->>'content' like 'H%'
ERROR:  cannot extract field from a non-object

这是公平的,但这个查询也是如此:

> select * from (select * from Table where header->>'type'='A') where body->>'content' like 'H%'

而这些不是:

> select * from Table where type='A' and body->>'content' like 'H%'
> select * from Table where header->>'type'='A' and body->>'content'='Hello'

我针对这个特定情况(其中'喜欢'谓词不正确地给予优先权)有解决办法,但我担心的是我显然甚至不能依靠括号来控制评估顺序即使在这种情况下,它改变了对数据应用的约束。有没有通用的方法呢?

2 个答案:

答案 0 :(得分:4)

您应该能够使用case

强制执行评估订单
select *
from Table
where (case when header->>'type'='A'
            then (case when body->>'content' like 'H%' then 1 end)
       end) = 1;

这是我唯一一次建议在case条款中添加where语句。

您还可以使用CTE保证订单:

with t as (
      select t.*
      from table t
      where header->>'type'='A'
     )
select t.*
from t
where body->>'content' like 'H%';

但是,这会产生实现中间结果的额外开销。

答案 1 :(得分:3)

(这是补充信息;戈登的答案应该标记正确)。

在SQL中,DB可以按任何顺序自由执行谓词。没有短路。带括号的组覆盖默认运算符优先级以控制哪些运算符绑定到哪些操作数,但不强制执行顺序。

子查询不保证执行顺序的任何内容。它可以并且应该在可能的情况下通过将其展平为外部查询来优化。

您遇到的问题与处理除以零的问题相同,并且具有相同的解决方案。

正确的解决方案是强制执行CASE的执行顺序,正如戈登所示。您可以使用CTE(WITH)或OFFSET 0黑客,但两者都会产生性能影响。