pgSQL FULL OUTER JOIN 'WHERE' 条件

pgSQL FULL OUTER JOIN 'WHERE' Condition

我正在尝试创建一个查询来检索当前价格和特卖价 如果促销是 运行;当没有促销时,我希望 store_picture_monthly_price_special.price AS special_price 到 return as null

在添加第二个 WHERE 条件之前,查询按照我的预期执行:store_picture_monthly_price_special.price return s 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 条件视为 "all or none"。如果没有销售 运行 则没有结果。

是否可以调整此查询,以便我每次都能获得正常定价和特价促销价格,当特价商品为 运行 或 returning null我想完成的是不是需要子查询?

这是我目前拥有的查询:

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

Table "store_picture_monthly"

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

Table 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

Table 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

任何时候在外连接的 table 上放置 where 谓词,它都会将外连接转换为内连接,因为外连接引入的空值永远无法与任何内容进行比较以生成true(因此外连接在行不匹配的地方放置了一堆带空值的行,然后 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'

要解决这个问题,请将谓词从 where 移到 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 中本机存在的空值和连接失败引入的空值以匹配来自 b 的行和来自 a 的行(除非我们还查看连接的 id 列的空值)

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 是否为空,尽管行数相同。如果我们计算 b.id,我们的计数可能会出错。了解连接行为的这种细微差别很重要。你甚至可能想要它,如果你想排除第 3 行但包括第 2 行,通过制作 a LEFT JOIN b ON a.id=b.id WHERE b.col = 'value' OR b.id IS NULL 的 where 子句 - 这将保留第 2 行但排除第 3 行,因为即使连接成功找到 b.id of 3 它不被任一谓词保存

问题的描述表明您需要 LEFT JOIN,而不是 FULL JOINFULL JOIN 非常罕见,尤其是在具有明确定义的外键关系的数据库中。

在您的情况下,WHERE 子句无论如何都会将您的 FULL JOIN 变成 LEFT JOIN,因为 WHERE 子句需要来自第一个 [=38] 的有效值=].

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();

备注:

  • 我引入了 table 别名,以便查询更易于编写和阅读。
  • 销售日期的条件现在在 ON 条款中。
  • 我删除了 GROUP BY。好像没必要。如果是,您可以使用 SELECT DISTINCT 代替。而且,如果需要的话,我会调查数据问题。
  • 我对日期比较持怀疑态度。 NOW() 有一个时间部分。比较列的命名表明它们只是没有时间的日期。