今天我在PostgreSQL中遇到了一些无法解释的(对我来说)行为-LEFT OUTER JOIN
不返回主表的记录(与空连接一个字段),如果在WHERE
表达式中使用了连接表字段
为了更容易地掌握案例细节,我将提供一个示例。因此,假设我们有2个表:item
包含一些商品,price
引用item
,包含不同年份的商品价格:
CREATE TABLE item(
id INTEGER PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE price(
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
year INTEGER NOT NULL,
value INTEGER NOT NULL,
CONSTRAINT goods_fk FOREIGN KEY (item_id) REFERENCES item(id)
);
表item
有2个记录(电视机和录像机物品),和表price
有3个记录,电视机的价格在2000年和2010年和2000年价格录像机只:
INSERT INTO item(id, name)
VALUES
(1, 'TV set'),
(2, 'VCR');
INSERT INTO price(id, item_id, year, value)
VALUES
(1, 1, 2000, 290),
(2, 1, 2010, 270),
(3, 2, 2000, 770);
-- no price of VCR for 2010
现在让我们做一个LEFT OUTER JOIN
查询,获取2010年所有商品的价格:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p ON i.id = p.item_id
WHERE p.year = 2010 OR p.year IS NULL;
由于某种原因,该查询将只返回电视机的结果,该电视机有今年的价格。结果中没有VCR记录:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
(1 row)
经过一些实验,我找到了一种方法来使查询返回我需要的结果(item
表的所有记录,在连接表的字段中为空,以防有一年没有任何记录。它是通过将年份过滤移动到JOIN
条件中实现的:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN (
SELECT * FROM price
WHERE year = 2010 -- <= here I filter a year
) p ON i.id = p.item_id;
现在的结果是:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
2 | VCR | |
(2 rows)
我的主要问题是-为什么第一个查询(在WHERE
中使用年份过滤)不像预期的那样工作,而是变成像INNER JOIN
一样的东西?
在我目前的项目中,这个问题严重阻碍了我,所以我也会感谢下一个相关问题的提示/提示:
- 是否有其他选择来达到适当的结果?
- …特别是-容易翻译成Django的ORM查询集?
Upd: @astentx建议将过滤条件直接移动到JOIN
中(它也有效):
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p
ON
i.id = p.item_id
AND p.year = 2010;
虽然,和我的第一个解决方案一样,我不知道如何用Django ORM查询集来表达它。还有其他建议吗?
第一个查询没有按预期工作,因为预期是错误的。它不像INNER JOIN那样工作。只有当VCR没有价格时,查询才返回VCR的记录。
SELECT
i.*,
y.year,
p.value
FROM item i
CROSS JOIN (SELECT 2010 AS year) y -- here could be a table
LEFT OUTER JOIN price p
ON (p.item_id = i.id
AND p.year = y.year);