“简洁版本
我有两张桌子:image
和item
。项目由两个图像组成:让我们称它们为left
和right
(项目的 GUI 可以是例如两个图像并排的画布)。我正在尝试设置有关图像的报告,显示哪个项目“使用”它们。
+------------+ +------------+
| image | | item |
+------------+ +------------+
| id <------------+ | id |
| name | | | name |
+------------+ +-------- im_left |
+-------- im_right |
+------------+
虽然如果一个项目只有一个对图像的引用,找出查询是微不足道的,但我很难将其扩展到“双重引用”情况,尤其是当一个项目引用了两次相同的图像时(考虑到我的情况,这是一个完全合法的场景业务限制)。
到目前为止,我使用两个LEFT OUTER JOIN
带有别名的 s 将图像连接到项目,使用左列和右列。但是当相同的图像同时用作左右时,这个构造就会失败(我无法真正解释结果,如下所示)。
由于这是数据库设计中非常常见的模式,在这种情况下,您将如何设计一个视图来显示每个图像以及它所使用的每个项目,以及它是从哪一列引用的?
例如
- image_1 被 item_1 和 item_2(左)和 item_3(右)使用
- image_2 被 none(左)和 item_4(右)使用
- image_3 被 item_5 和 item_6(左)和 item_6(右)使用
长版(可能过于本地化,但显示了我的试验和具体问题)。
这是我的 2 个表的定义:
CREATE TABLE image (
id serial PRIMARY KEY,
name text
);
INSERT INTO image(name) VALUES ('image 1');
INSERT INTO image(name) VALUES ('image 2');
INSERT INTO image(name) VALUES ('image 3');
CREATE TABLE item (
id serial PRIMARY KEY,
name text,
im_left int,
FOREIGN KEY (im_left) REFERENCES image(id),
im_right int,
FOREIGN KEY (im_right) REFERENCES image(id)
);
INSERT INTO item(name,im_left,im_right) VALUES ('item 1',1,2);
INSERT INTO item(name,im_left,im_right) VALUES ('item 2',1,3);
INSERT INTO item(name,im_left,im_right) VALUES ('item 3',2,3);
直到今天,我一直在使用这个查询来构建我的视图:
CREATE VIEW imagev_v1 AS (
SELECT image.id, image.name,
array_agg(li.id) AS left_ids,
array_agg(li.name) AS left_names,
array_agg(ri.id) AS right_ids,
array_agg(ri.name) AS right_names
FROM image
LEFT OUTER JOIN item AS li ON li.im_left=image.id
LEFT OUTER JOIN item AS ri ON ri.im_right=image.id
GROUP BY image.id, image.name
ORDER BY name ASC
);
它工作得很好:
SELECT * FROM imagev_v1;
id | name | left_ids | left_names | right_ids | right_names
----+---------+-------------+---------------------+-------------+---------------------
1 | image 1 | {1,2} | {"item 1","item 2"} | {NULL,NULL} | {NULL,NULL}
2 | image 2 | {3} | {"item 3"} | {1} | {"item 1"}
3 | image 3 | {NULL,NULL} | {NULL,NULL} | {2,3} | {"item 2","item 3"}
(3 rows)
直到我添加了一个在左右列中引用相同图像的偷偷摸摸的项目:
INSERT INTO item(name,im_left,im_right) VALUES ('item 4',3,3);
SELECT * FROM imagev_v1;
id | name | left_ids | left_names | right_ids | right_names
----+---------+----------+------------------------------+-------------+------------------------------
1 | image 1 | {1,2} | {"item 1","item 2"} | {NULL,NULL} | {NULL,NULL}
2 | image 2 | {3} | {"item 3"} | {1} | {"item 1"}
3 | image 3 | {4,4,4} | {"item 4","item 4","item 4"} | {2,3,4} | {"item 2","item 3","item 4"}
(3 rows)
至少可以说,第三条结果行很奇怪,但我无法解释这种行为。
我尝试了另一个版本的视图,它可以工作,但无法显示引用的来源(图像是否由im_left
orim_right
列引用):
CREATE VIEW imagev_v2 AS (
SELECT image.id, image.name,
array_agg(item.id) AS item_ids,
array_agg(item.name) AS item_names
FROM image
LEFT OUTER JOIN item ON item.im_left=image.id OR item.im_right=image.id
GROUP BY image.id, image.name
ORDER BY name ASC
);
SELECT * FROM image_v2 ;
id | name | item_ids | item_names
----+---------+----------+------------------------------
1 | image 1 | {1,2} | {"item 1","item 2"}
2 | image 2 | {1,3} | {"item 1","item 3"}
3 | image 3 | {2,3,4} | {"item 2","item 3","item 4"}
(3 rows)
感谢您阅读至此,现在您有权看到实际问题:我怎样才能写出始终imagev_v3
正确的内容(与image_v1
image_v2
请注意,我使用的是 PostgreSQL 8.4,但我认为它应该是无关紧要的。