pgSQL FULL OUTER JOIN'WHERE'条件

时间:2019-03-16 04:09:48

标签: sql postgresql outer-join full-outer-join

我正在尝试创建一个查询以检索当前价格和特价[strong]如果正在进行销售;当没有销售时,我希望store_picture_monthly_price_special.price AS special_price以的价格返回。

在添加第二个 WHERE 条件之前,查询将按照我的期望执行: store_picture_monthly_price_special.price 返回 null ,因为没有销售目前。

 store_picture_monthly_reference | tenure |   name    | regular_price | special_price 
                               3 |     12 | 12 Months |        299.99 | {Null}             
                               2 |      3 | 3 Months  |         79.99 | {Null}            
                               1 |      1 | 1 Month   |         29.99 | {Null}            

pgSQL将第二个 WHERE 条件视为“全部或全部”。如果没有销售,将没有结果。

是否可以调整此查询,以便每次运行时都能获得正常定价,并且特价运行时以美元价值获得特价或返回,这是我要执行的操作完成需要子查询吗?

这是我目前的查询方式:

SELECT store_picture_monthly.reference AS store_picture_monthly_reference , store_picture_monthly.tenure , store_picture_monthly.name , store_picture_monthly_price_regular.price AS regular_price , store_picture_monthly_price_special.price AS special_price

FROM ( store_picture_monthly INNER JOIN store_picture_monthly_price_regular ON store_picture_monthly_price_regular.store_picture_monthly_reference = store_picture_monthly.reference )

FULL OUTER JOIN store_picture_monthly_price_special ON store_picture_monthly.reference = store_picture_monthly_price_special.store_picture_monthly_reference

WHERE 
    ( store_picture_monthly_price_regular.effective_date < NOW() ) 
    AND
    ( NOW() BETWEEN store_picture_monthly_price_special.begin_date AND store_picture_monthly_price_special.end_date )

GROUP BY store_picture_monthly.reference , store_picture_monthly_price_regular.price , store_picture_monthly_price_regular.effective_date , store_picture_monthly_price_special.price

ORDER BY store_picture_monthly_price_regular.effective_date DESC

表“ store_picture_monthly

reference bigint,
name text,
description text,
tenure bigint,
available_date timestamp with time zone,
available_membership_reference bigint

store_picture_monthly_price_regular

reference bigint ,
store_picture_monthly_reference bigint,
effective_date timestamp with time zone,
price numeric(10,2),
membership_reference bigint

store_picture_monthly_price_special

reference bigint,
store_picture_monthly_reference bigint,
begin_date timestamp with time zone,
end_date timestamp with time zone,
price numeric(10,2),
created_date timestamp with time zone DEFAULT now(),
membership_reference bigint

2 个答案:

答案 0 :(得分:1)

每当您在外部联接的表上放置一个位置谓词时,它都会将外部联接转换为内部联接,因为外部联接引入的空值永远无法与任何产生真值的事物进行比较(因此,外部联接将负载为null的行放在行不匹配的地方,然后WHERE再次取出整行)

考虑以下简单示例:

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id
WHERE b.col = 'value'

等同于:

SELECT * FROM 
a INNER JOIN b ON a.id = b.id
WHERE b.col = 'value'

要解决此问题,请将谓词移出位置并移至ON

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id AND b.col = 'value'

您还可以考虑:

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id
WHERE b.col = 'value' OR b.col IS NULL

但是,如果b.col自然包含一些空值,这可能会获取您不想要的数据;它无法区分b.col中本机存在的null和联接失败中引入的null,以将b中的行与a中的行匹配(除非我们还查看联接的id列的空)。 / p>

A
id
1
2
3

B
id, col
1, value
3, null

--wrong, behaves like inner
A left join B ON a.id=b.id WHERE b.col = 'value'
1, 1, value

--maybe wrong, b.id 3 might be unwanted
A left join B ON a.id=b.id WHERE b.col = 'value' or b.col is null
1, 1, value
2, null, null
3, 3, null

--maybe right, simpler to maintain than the above
A left join B ON a.id=b.id AND b.col = 'value' 
1, 1, value
2, null, null
3, null, null

在这最后两个中,区别在于b.id是否为null,尽管行数相同。如果我们在计算b.id,我们的计数可能最终会错误。重要的是要了解联接行为的这种细微差别。如果您想通过制作a LEFT JOIN b ON a.id=b.id WHERE b.col = 'value' OR b.id IS NULL的where子句来排除第3行但包括第2行,则可能甚至想要它-这将保留第2行但排除第3行,因为即使联接成功找到了b .id为3,两个谓词均不保留

答案 1 :(得分:1)

该问题的描述表明您想要LEFT JOIN,而不是FULL JOINFULL JOIN很少见,特别是在具有明确定义的外键关系的数据库中。

在您的情况下,WHERE子句始终将您的FULL JOIN变成LEFT JOIN,因为WHERE子句需要第一个表中的有效值。

SELECT spm.reference AS store_picture_monthly_reference, 
       spm.tenure, spm.name, 
       spmpr.price AS regular_price,
       spmps.price AS special_price
FROM store_picture_monthly spm INNER JOIN
     store_picture_monthly_price_regularspmpr
     ON spmpr.store_picture_monthly_reference = spm.reference LEFT JOIN
     store_picture_monthly_price_special spmps 
     ON spm.reference = spmps.store_picture_monthly_reference AND
        NOW() BETWEEN spmps.begin_date AND spmps.end_date
WHERE spmpr.effective_date < NOW();

注意:

  • 我引入了表别名,以便查询更易于编写和阅读。
  • 销售日期的条件现在在ON子句中。
  • 我删除了GROUP BY。似乎没有必要。如果是这样,则可以改用SELECT DISTINCT。而且,如果需要的话,我会调查数据问题。
  • 我对日期比较感到怀疑。 NOW()具有时间成分。比较列的名称表明,这些只是没有时间的日期。