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 JOIN
。 FULL 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()
有一个时间部分。比较列的命名表明它们只是没有时间的日期。
我正在尝试创建一个查询来检索当前价格和特卖价 如果促销是 运行;当没有促销时,我希望 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 JOIN
。 FULL 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()
有一个时间部分。比较列的命名表明它们只是没有时间的日期。