SQL進階教程1-7 用SQL進行集合運算: 比較兩個表是否相等、用差集實現關系除法運算、尋找相等的子集


  集合運算符的參數是集合,從數據庫實現層面上來說就是表或者視圖。

  注意事項:

  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在比較兩個集合時,並不是以行為單位來比較的,而是將集合當做整體來處理的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM