2013-04-08 59 views
1

「短」版查看從同一個表

兩個外鍵引用的表我有兩個表:imageitem。項目由兩個圖像組成:讓我們稱它們爲leftright(項目的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用於通過無(左)和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_leftim_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,但我認爲它應該是非常不相關的。

回答

1

使用具有多個謂詞的連接。它可以是一個內部連接,但是如果您想要查看任何項目未使用的圖像,請將其作爲外部連接,如下所述。

Select i.serial imgId, i.text imgName, 
    case when t.im_left is Null and t.im_right Is Null then 'None' 
     When t.im_left Is Null Then 'Right' 
     When t.im_right Is Null Then 'Left' Else 'Both' End Source, 
    t.serial itmId, t.text itmName 
From Image i 
    Left Join item t 
    On t.im_left = i.serial Or 
     t.im_right = i.serial 
+0

Aaahh,所以此案的事情是的伎倆!非常感謝您的洞察力(和時間)。 – Tibo 2013-04-08 14:27:42

+0

案例解決了這麼多問題.... – 2013-04-08 15:04:13

0

這是另一種解決方案,可以爲你工作,類似於你第一次嘗試。你只需要在你的左側和右側分開連接。

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 
(
    SELECT 
     image.id as image_id, 
     image.name as image_name, 
     li.id as li_id, 
     li.name as li_name, 
     NULL as ri_id, 
     NULL as ri_name 
    FROM image 
    LEFT OUTER JOIN item AS li ON li.im_left=image.id 
    UNION ALL 
    SELECT 
     image.id as image_id, 
     image.name as image_name, 
     NULL as li_id, 
     NULL as li_name, 
     ri.id as ri_id, 
     ri.name as ri_name 
    FROM image 
    LEFT OUTER JOIN item AS ri ON ri.im_right=image.id 

) image 

GROUP BY image_id, image_name 
ORDER BY image_name ASC 

SQLFiddle Demo

0

(由OP,下面編輯拒絕向@Charles Betana的答案)

這裏是查理的回答,略作修改,以反映的問題。

CREATE VIEW imagev_v3 AS (
    SELECT i.id, i.name, 
     array_agg(CASE 
        WHEN t.im_left<>i.id AND t.im_right<>i.id THEN 'None' 
        WHEN t.im_left=i.id AND t.im_right<>i.id THEN 'Left' 
        WHEN t.im_left<>i.id AND t.im_right=i.id THEN 'Right' 
        ELSE 'Both' 
       END) AS item_sources, 
     array_agg(t.id) AS item_ids, 
     array_agg(t.name) AS item_names 
    FROM image AS i 
     LEFT OUTER JOIN item AS t 
      ON t.im_left = i.id OR t.im_right = i.id 
    GROUP BY i.id, i.name 
); 

其產生正確答案:

SELECT * FROM imagev_v3 ; 
id | name | item_sources | item_ids |   item_names 
----+---------+--------------------+----------+------------------------------ 
    1 | image 1 | {Left,Left}  | {1,2} | {"item 1","item 2"} 
    2 | image 2 | {Right,Left}  | {1,3} | {"item 1","item 3"} 
    3 | image 3 | {Right,Right,Both} | {2,3,4} | {"item 2","item 3","item 4"} 
(3 rows)