集合運算符的參數是集合,從數據庫實現層面上來說就是表或者視圖。
注意事項:
1.SQL能夠操作具有重復行的集合,可以通過可選項ALL來支持。
一般的集合論是不允許集合里面存在重復元素的,因此集合{1,1,2,3,3}和集合{1,2,3}被視為相同的集合,但是關系數據庫的表允許存在重復的行,稱為多重集合。
ALL的作用和SELECT子句里面的DISTINCT可選項剛好相反。
1.SQL集合運算符也提供了允許重復和不允許重復的兩種用法,如果直接使用UNION或者INTERSECT,結果里就不會出現重復的行。
如果想在結果里面留下重復行,可以加上可選項ALL。寫作UNION ALL。
2. 集合運算符為了排除掉重復行,默認地會發生排序,而加上可選項ALL之后,就不再排序,所以性能會有提升。這是常用的非常有效地用於優化查詢性能的方法,
所以如果不關心是否存在重復行,或者確定結果里面不會產生重復行,加上可選項ALL會更好些。
2.集合運算符有優先級
標准SQL規定,INTERSECT比UNION和EXCEPT優先級更高,因此,當同事使用UNION和INTERSECT,又想讓UNION優先執行時,必須用括號明確地指定運算順序。
3.各個DBMS提供商在集合運算的實現程度上參差不齊。
早期的SQL對集合運算的支持程度不是很高,受到這一影響,各個數據庫提供商的實現程度也參差不齊。
SQL Server從2005版開始支持INTERSECT 和EXCEPT,而MySQL還都不支持。 還有像Oracle這樣,實現了EXCEPT功能但是卻命名為MINUS的數據庫。
4.除法運算沒有標准定義。
四則運算里面的 和(UNION) 差(EXCEPT) 積(CROSS JOIN) 商都被引入了標准的SQL,但是商並沒有標准化,需要自己寫SQL實現。
5.比較表和表
在遷移數據庫的時候,或者需要比較備份數據和最新數據的時候,我們需要調查兩張表是否是相等的。
這里所說的“相等”指的是行數和列數以及內容都相同,即“是容一個集合”的意思。
如何使用SQL確定兩張表是相等還是不相等呢?
方法一:假設已經實現確定了tbl_A和tbl_B的行數是一樣的,如果行數不一樣,就不需要再比較其他的了。
SQL:
SELECT COUNT(*) AS row_cnt FROM (SELECT * FROM tbl_A UNION ELECT * FROM tbl_B) TMP;
如果此函數的執行結果與A或者B的行數不一致,說明A與B是不相等的,否則兩張表相等。
我們也可以只比較表里的一部分列或者一部分行,在WHERE子句中加入過濾條件就可以比較了。
需要注意的是,此條SQL語句對於一個自身擁有重復行的表是不生效的,由此,我們應該明白主鍵對表來說是多么的重要。
方法二:
在集合里,判定連個集合是否相等時,一般使用下面這兩種方法:
如果集合A和集合B相等,那么 A UNION B = A = B = A INTERSECT B 都是成立的,剩下的問題是對A和B 分別進行UNION運算和INTERSECT運算后,如何比較這兩個結果。
因為我們能夠確定的是
因此只要判定 (A UNION B)EXCEPT (A INTERSECT B) 的結果集是不是空集就可以了,如果A=B,則這個結果集是空集,否則這個結果集里面肯定有數據。
SELECT CASE WHEN COUNT(*) = 0 THEN '相等' ELSE '不相等' END AS result FROM ( (SELECT * FROM tbl_A UNION SELECT * FROM tbl_B) EXCEPT (SELECT * FROM tbl_A INTERSECT SELECT * FROM tbl_B) )
此條SQL語句不用事先知道表的行數,但是有一些缺陷
1.性能有所下降,因為這里需要進行4次排序:3次集合運算加上1次DISTINCT
2.因為這里使用了INTERSECT 和EXCEPT,所以目前這條SQL不能在MySQL中執行。
輸出兩張表中不同的部分:
(SELECT * FROM tbl_A EXCEPT SELECT * FROM tbl_B) UNION (SELECT * FROM tbl_B EXCEPT SELECT * FROM tbl_A)
6.用差集實現關系除法運算
進行除法運算,方法比較多,其中具有代表性的是下面三個:
1.嵌套使用NOT EXISTS;
2.使用HAVING子句轉換成一對一關系;
3. 將除法變成減法;
將除法變成減法:
SELECT DISTINCT emp FROM EmpSkills ES1 WHERE NOT EXISTS (SELECT skill FROM Skills EXCEPT SELECT skill FROM EmpSkills ES2 WHERE ES1.emp = ES2.emp);
關聯子查詢是為了使SQL能夠實現類似面向過程語言中的循環功能引入的。上面這條SQL語句的處理方法與面向過程語言里面的循環、終端控制處理很像。
將兩張表當成兩個文件,然后一行行循環處理,針對某一個員工循環判斷各種技術的掌握情況。如果存在企業需求的技術,就進行減法運算;如果不存在就中止
該員工的循環,繼續對下一個員工執行同樣的處理。
7.尋找相同的子集
現在想要找出,經營的零件在種類數和種類上都完全相同的供應商組合,答案是A-C B-D這兩組。
SQL並沒有任何用於檢查集合的包含關系或者相等性的謂詞,IN謂詞只能用來檢查元素是否屬於某個集合,而不能檢查集合是否是某個集合的子集。
對於連個不同的供應商,我們應該判斷下面這兩個條件:
1.連個供應商都經營同種類型的零件;
2.兩個供應商經營的零件種類數相同。
SELECT SP1.sup AS s1, SP2.sup AS s2 FROM SupParts SP1, SupParts SP2 WHERE SP1.sup < SP2.sup -- 生成供應商的全部組合 AND SP1.part = SP2.part -- 條件1 :經營同種類型的零件 GROUP BY SP1.sup, SP2.sup HAVING COUNT(*) = (SELECT COUNT(*) -- 條件2 :經營的零件種類數相同 FROM SupParts SP3 WHERE SP3.sup = SP1.sup) AND COUNT(*) = (SELECT COUNT(*) FROM SupParts SP4 WHERE SP4.sup = SP2.sup);
對於Having中的兩組Count(*)比較,每組Count(*)比較中:
前面的COUNT(*)是SP1和SP2不同供應商但是供應相同類型的零件的數量,后面的COUNT(*)是SP1或者SP2中該供應商供應的所有配件數量
COUNT(*)的比較充分告訴我們,SQL在比較兩個集合時,並不是以行為單位來比較的,而是將集合當做整體來處理的。