SQL-3標準中提供了三種對檢索結果進行集合運算的命令:並集UNION;交集INTERSECT;差集EXCEPT(在Oracle中叫做 MINUS)。在有些數據庫中對此的支持不夠充分,如MySql中只有UNION,沒有其他兩種。實際上這些運算都可以通過普通的SQL來實現,雖然有時有些繁瑣。
假設有兩個表(或視圖)s,t,s中有兩個字段sa,sb;t中有兩個字段ta,tb;
差集EXCEPT:
-
SELECTsaFROMs
-
EXCEPT
-
SELECTtaFROMt;
可以寫作
-
SELECTsaFROMs
-
WHEREsaNOTIN
-
(SELECTtaFROMt)
上面的例子中忽略了對s和t單獨的條件,這些總可以加入AND條件完成,或者使用視圖。如果是多個字段比較麻煩,如:
-
SELECTsa, sbFROMs
-
EXCEPT
-
SELECTta, tbFROMt;
需要寫成
-
SELECTsa, sbFROMs
-
WHERE(sa, sb)NOTIN
-
(SELECTta, tbFROMt)
上面使用的語法不見得數據庫都支持。好在不支持EXCEPT的MySQL支持這種語法,而不支持這種語法的MSSQL又支持EXCEPT。
注意對於這樣的row constructors(Mysql術語),是和下面寫法(以及其他類似寫法)不等價的。
-
SELECTsa, sbFROMs
-
WHEREsaNOTIN
-
(SELECTtaFROMt)
-
ANDsbNOTIN
-
(SELECTtbFROMt)
在MSSQL中的一個解決技巧是,把這兩個字段(假設字符類型)拼起來,即
-
SELECTsa, sbFROMs
-
WHEREsa+sbNOTIN
-
(SELECTta+tbFROMt)
交集INTERSECT:
-
SELECTsaFROMs
-
INTERSECT
-
SELECTtaFROMt;
可以寫成
-
SELECTsaFROMs
-
WHEREsa IN
-
(SELECTtaFROMt)
當然也可以寫成
-
SELECTsaFROMs
-
WHEREEXISTS
-
(SELECT*FROMtWHEREt.ta=s.sa)
或者使用連接
-
SELECTsaFROMs, t
-
WHEREsa = ta
實際上這幾個語句都有點問題,就是INTERSECT在出現重複時的語義問題。按照SQL-3標準,類似UNION,可以有明確的 INTERSECT ALL或者INTERSECT DISTINCT語法。一般的INTERSECT實現並沒有明確這一點,而且從邏輯上講意義也不大。那麼當s或t中出現重複的時,如sa='x'的有2 個,sb='x'的有3個,使用上面的子查詢將返回2行,使用連接將返回6行,當然這兩個語句都可以加上一個DISTINCT,就實現了 INTERSECT DISTINCT語義了。
並集UNION:
MySql從4.0開始就支持UNION(ALL 和 DISTINCT)了,爲完整起見,也列舉一下。
其實實現這樣一個結果是很麻煩的
-
SELECTsaFROMs
-
UNIONDISTINCT
-
SELECTtaFROMt;
需要使用外連接,而且是Full的外連接
-
SELECTDISTINCTNVL(s.sa, t.ta)
-
FROMs FULLOUTERJOINtON(s.sa=t.ta)
上面的例子中我使用了Oracle的語法,實際上MySql不支持FULL OUTER JOIN(雖然支持LEFT和RIGHT OUTER JOIN),好在MySql支持UNION。
對於UNION ALL語義,我還沒有想出來用普通查詢如何實現,如果在上面語句中去掉DISTINCT,結果肯定不對。