Postgres:numrange()在某些计划下中断

时间:2017-06-23 11:37:03

标签: postgresql

此查询过去效果不错但现在却造成了麻烦:

SELECT
    incoming.*,
    prescribed.*
FROM
    "CRM_DEAL_INCOMING_RANGES_M" incoming
LEFT JOIN 
    "CRM_DEAL_PRESCRIBED_RANGES_M" prescribed 
        -- join the two tables
        ON prescribed.klient = incoming.klient 
        AND prescribed.op = incoming.op 
        -- make sure ranges are correct - there really is data where range_to > range_from, we just want to skip it
        AND prescribed.range_from <= prescribed.range_to
        AND incoming.range_from <= incoming.range_to
        AND numrange(prescribed.range_from, prescribed.range_to) 
            && numrange(incoming.range_from, incoming.range_to)
WHERE incoming.klient = 4;

请注意,我们确保范围边界的顺序正确。

查询以以下错误结束:ERROR: range lower bound must be less than or equal to range upper bound

但是,如果删除WHERE条件,则效果很好!

问题的解释是here,工作备选方案(不包括WHERE)的解释是here

当规划师可以调整条件时,如何过滤掉numrange()会阻塞的数据?

编辑:现在我只是将连接条件的第二部分从JOIN移到WHERE中,但我觉得它只是躲避计划者一段时间。

SELECT
    incoming.*,
    prescribed.*
FROM
    "CRM_DEAL_INCOMING_RANGES_M" incoming
LEFT JOIN 
    "CRM_DEAL_PRESCRIBED_RANGES_M" prescribed 
        -- join the two tables
        ON prescribed.klient = incoming.klient 
        AND prescribed.op = incoming.op 
WHERE 
  incoming.klient = 4
  -- make sure ranges are correct - there really is data where range_to > range_from, we just want to skip it
  AND prescribed.range_from <= prescribed.range_to
  AND incoming.range_from <= incoming.range_to
  AND numrange(prescribed.range_from, prescribed.range_to) 
    && numrange(incoming.range_from, incoming.range_to);

(正如a_horse_with_no_name正确指出的,这与原始查询有不同的语义 - 但在我的特定情况下并不重要)

1 个答案:

答案 0 :(得分:1)

没有保证加入条款,WHERE条款等将以任何给定的顺序执行。如果你的查询依赖于它,那就是它的错误。

在这种情况下,正确的解决方案是a CASE expression,其中(主要)强制排序。

CASE
WHEN prescribed.range_from <= prescribed.range_to
     AND incoming.range_from <= incoming.range_to
     THEN numrange(prescribed.range_from, prescribed.range_to) 
          && numrange(incoming.range_from, incoming.range_to)
END;

请注意,此处不需要ELSE子句,因为CASE如果没有子句匹配则生成NULL,而NULL对于WHERE子句则为false。

这也可能会搞砸索引选择,但大多数其他方法也是如此,例如使用OFFSET 0或(ab)使用CTE的子查询。

我认为它在反转范围内对numrangeERROR有误,而不是像翻转它们或评估为null那样,正是因为它提出了这样的疑问很难写。