SQL中的全外連接

今天看到一貼, 說到 MySQL 不支持全外連接, 希望能給出一個方法能夠解決這個問題.

我們首先必須明白在數學領域, set 和 bag 是兩種不同的概念. bag 比 set 更加複雜. 主要就是允許了元素的重複出現. 因此 bag 的union, intersect, difference 也就有了些區別.

要模擬全外連接, 我們需要藉助: 左外連接(右外連接是左外連接的對稱運算...), UNION ALL, EXCEPT ALL.

爲什麼要用 UNION ALL, EXCEPT ALL? 因爲 JOIN 操作產生的結果集是 bag, 因此必須使用 bag 運算符.

我沒有花太多時間設計測試用例, 這裏只是簡單的給出幾個錯誤的結果, 希望大家平時應用小心.

SQL SERVER 2005 雖然支持全外連接, 但是不支持 EXCEPT ALL, 因此無法模擬這個過程. 需要注意使用混合使用 bag 和 set 的運算符將會對結果產生影響. 除非你清楚你的目的, 否則永遠不要把它們混合在一起.

下面是測試用例的準備:

CREATE TABLE a(c INT);
CREATE TABLE b(c INT);
INSERT INTO a VALUES(1);
INSERT INTO a VALUES(2);
INSERT INTO a VALUES(3);
INSERT INTO b VALUES(1);
INSERT INTO b VALUES(1);
INSERT INTO b VALUES(2);
INSERT INTO b VALUES(4);
下面是在SQL Server 2005中執行的標準輸出結果(5條!): 
SELECT * FROM a FULL JOIN b ON a.c = b.c;
c           c
----------- -----------
1           1
1           1
2           2
3           NULL
NULL        4
(5 row(s) affected)
我們看到了兩行(1,1), 這就是我們需要注意的 bag 才具有的行爲. 下面是幾個典型的錯誤語句:
SELECT * FROM a LEFT JOIN b ON a.c = b.c UNION SELECT * FROM a RIGHT JOIN b ON a.c = b.c;
c           c
----------- -----------
NULL        4
1           1
2           2
3           NULL
(4 row(s) affected)
SELECT * FROM a LEFT JOIN b ON a.c = b.c UNION ALL SELECT * FROM a RIGHT JOIN b ON a.c = b.c;
c           c
----------- -----------
1           1
1           1
2           2
3           NULL
1           1
1           1
2           2
NULL        4
(8 row(s) affected)
SELECT a.c, b.c FROM a LEFT JOIN b ON a.c = b.c
UNION
SELECT a.c, b.c FROM b LEFT JOIN a ON a.c = b.c
WHERE a.c IS NULL;
c           c
----------- -----------
NULL        4
1           1
2           2
3           NULL
(4 row(s) affected)
正確的應該是:
SELECT * FROM a LEFT JOIN b ON a.c = b.c UNION ALL SELECT * FROM a RIGHT JOIN b ON a.c = b.c EXCEPT ALL SELECT * FROM a INNER JOIN b ON a.c = b.c;
希望大家小心 bag 和 set 的區別. 你還可以指定 DISTINCT 消除重複元組. 但是你必須清楚這些操作的影響, 有人說 bag 操作是 SQL 語言最大的缺點... 我倒覺得仁者見仁, 智者見智. 就好像有人說 '第一範式' 關於不應該有 NULL 值的問題, 認爲既然最初允許了 NULL 爲什麼第一範式就要禁止呢? 
最後大家要注意 wikipedia 上關於 JOIN 的說明並沒有考慮 bag 的情況, 而是當作 set 給出的例子. 希望不是 wikipedia 的錯誤, 端午過後, 我再去仔細求證一下. 
發佈了110 篇原創文章 · 獲贊 10 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章