SQL集合運算 差集 並集 交集

SQL-3標準中提供了三種對檢索結果進行集合運算的命令:並集UNION;交集INTERSECT;差集EXCEPT(在Oracle中叫做 MINUS)。在有些數據庫中對此的支持不夠充分,如MySql中只有UNION,沒有其他兩種。實際上這些運算都可以通過普通的SQL來實現,雖然有時有些繁瑣。

假設有兩個表(或視圖)s,t,s中有兩個字段sa,sb;t中有兩個字段ta,tb;

差集EXCEPT:

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. EXCEPT
  3. SELECTtaFROMt;

可以寫作

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. WHEREsaNOTIN
  3.    (SELECTtaFROMt)

上面的例子中忽略了對s和t單獨的條件,這些總可以加入AND條件完成,或者使用視圖。如果是多個字段比較麻煩,如:

PLAIN TEXT
SQL:
  1. SELECTsa, sbFROMs
  2. EXCEPT
  3. SELECTta, tbFROMt;

需要寫成

PLAIN TEXT
SQL:
  1. SELECTsa, sbFROMs
  2. WHERE(sa, sb)NOTIN
  3.    (SELECTta, tbFROMt)

 

上面使用的語法不見得數據庫都支持。好在不支持EXCEPT的MySQL支持這種語法,而不支持這種語法的MSSQL又支持EXCEPT。

注意對於這樣的row constructors(Mysql術語),是和下面寫法(以及其他類似寫法)不等價的。

PLAIN TEXT
SQL:
  1. SELECTsa, sbFROMs
  2. WHEREsaNOTIN
  3.    (SELECTtaFROMt)
  4. ANDsbNOTIN
  5.    (SELECTtbFROMt)

在MSSQL中的一個解決技巧是,把這兩個字段(假設字符類型)拼起來,即

PLAIN TEXT
SQL:
  1. SELECTsa, sbFROMs
  2. WHEREsa+sbNOTIN
  3.    (SELECTta+tbFROMt)

 

交集INTERSECT:

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. INTERSECT
  3. SELECTtaFROMt;

可以寫成

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. WHEREsa IN
  3.    (SELECTtaFROMt)

當然也可以寫成

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. WHEREEXISTS
  3.    (SELECT*FROMtWHEREt.ta=s.sa)

或者使用連接

PLAIN TEXT
SQL:
  1. SELECTsaFROMs, t
  2. 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)了,爲完整起見,也列舉一下。
其實實現這樣一個結果是很麻煩的

PLAIN TEXT
SQL:
  1. SELECTsaFROMs
  2. UNIONDISTINCT
  3. SELECTtaFROMt;

需要使用外連接,而且是Full的外連接

PLAIN TEXT
SQL:
  1. SELECTDISTINCTNVL(s.sa, t.ta)
  2. FROMs FULLOUTERJOINtON(s.sa=t.ta)

 

上面的例子中我使用了Oracle的語法,實際上MySql不支持FULL OUTER JOIN(雖然支持LEFT和RIGHT OUTER JOIN),好在MySql支持UNION。

對於UNION ALL語義,我還沒有想出來用普通查詢如何實現,如果在上面語句中去掉DISTINCT,結果肯定不對。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章