1. 程式人生 > >SQL(2)

SQL(2)

介紹多表查詢等複雜SQL語句。

關係資料庫的查詢結果都是一個結果表(也是關係)

集聚函式

基本語法

  • 統計元組個數
    • COUNT(*)
  • 統計一列中值的個數
    • COUNT([DISTINCT|ALL]<列名>)
  • 計算一列值的總和(此列必須為數值型)
    • SUM([DISTINCT|ALL]<列名>)
  • 計算一列值的平均值(此列必須為數值型)
    • AVG([DISTINCT|ALL]<列名>)
  • 求一列中的最大值和最小值
    • MAX([DISTINCT|ALL]<列名>)
    • MIN([DISTINCT|ALL]<列名>)

例子

  • 查詢選修1號課程的學生最高分數

    • SELECTMAX(Grade)
         FROM SC
         WHERE Cno='1';
      
  • 查詢學生201215012選修課程的總學分數

    • SELECT SUM(Ccredit)
          FROM  SC,Course
          WHERE Sno='201215012'                
          AND
      SC.Cno=Course.Cno;

GROUP BY 子句

細化聚集函式的作用物件

  • 如果未對查詢結果分組,聚集函式將作用於整個查詢結果
  • 對查詢結果分組後,聚集函式將分別作用於每個組
  • 按指定的一列或多列值分組,值相等的為一組

HAVING短語與WHERE子句的區別:

  • 作用物件不同
  • WHERE子句作用於基表或檢視,從中選擇滿足條件的元組
  • HAVING短語作用於組,從中選擇滿足條件的組
  • WHERE子句不能使用聚合函式!

例子

  1. 求各個課程號及相應的選課人數

       SELECT Cno,
    COUNT(Sno) FROM SC GROUP BY Cno;
  2. 查詢選修了3門以上課程的學生學號

    	SELECT Sno
         FROM  SC
         GROUP BY Sno
         HAVING  COUNT(*) >3;      
    
  3. 查詢平均成績大於等於90分的學生學號和平均成績

        SELECT  Sno, AVG(Grade)
        FROM  SC
        GROUP BY Sno
        HAVING AVG(Grade)>=90;
    

    這裡只能使用HAVING,不能使用WHERE

ORDER BY子句

  • 可以按一個或多個屬性列排序
    • 優先順序逐漸降低
  • 升序:ASC;
  • 降序:DESC;
  • 預設值為升序
  • 對於空值,排序時顯示的次序由具體系統實現來決定

例子

  1. 查詢選修了3號課程的學生的學號及其成績,查詢結果按分數降序排列

        SELECT Sno, Grade
        FROM    SC
        WHERE  Cno= ' 3 '
        ORDER BY Grade DESC;
    
  2. 查詢全體學生情況,查詢結果按所在系的系號升序排列,同一系中的學生按年齡降序排列

        SELECT  *
        FROM  Student
        ORDER BY Sdept, Sage DESC;  
    

連線查詢

  • 連線查詢:同時涉及兩個以上的表的查詢

  • 連線條件或連線謂詞:用來連線兩個表的條件

  • 一般格式:

    [<表名1>.]<列名1> <比較運算子> [<表名2>.]<列名2>

    [<表名1>.]<列名1>BETWEEN [<表名2>.]<列名2>AND[<表名2>.]<列名3>

  • 連線欄位:連線謂詞中的列名稱

    • 連線條件中的各連線欄位型別必須是可比的,但名字不必相同

(非)等值連線查詢

等值連線:連線運算子為=,這裡與Join操作等價。

例子

  1. 查詢每個學生及其選修課程的情況

        SELECT  Student.*, SC.*
        FROM     Student, SC
        WHERE  Student.Sno = SC.Sno;
    
  2. 一條SQL語句可以同時完成選擇和連線查詢,這時WHERE子句是由連線謂詞和選擇謂片語成的複合條件。

    查詢選修2號課程且成績在90分以上的所有學生的學號和姓名

        SELECT  Student.Sno, Sname
        FROM     Student, SC
        WHERE  Student.Sno=SC.Sno  AND    		               
               SC.Cno=' 2 ' AND SC.Grade>90;
    

執行過程

巢狀迴圈法(NESTED-LOOP)

  • 首先在表1中找到第一個元組,然後從頭開始掃描表2,逐一查詢滿足連線件的元組,找到後就將表1中的第一個元組與該元組拼接起來,形成結果表中一個元組。
  • 表2全部查詢完後,再找表1中第二個元組,然後再從頭開始掃描表2,逐一查詢滿足連線條件的元組,找到後就將表1中的第二個元組與該元組拼接起來,形成結果表中一個元組。
  • 重複上述操作,直到表1中的全部元組都處理完畢

可以發現,等值連線的複雜度很高,為O(m* n)。

自身連線

  • 自身連線:一個表與其自己進行連線
  • 需要給表起別名以示區別
  • 由於所有屬性名都是同名屬性,因此必須使用別名字首

例子

  1. 查詢每一門課的間接先修課(即先修課的先修課)

    	SELECT  FIRST.Cno, SECOND.Cpno
         FROM  Course  FIRST, Course  SECOND
         WHERE FIRST.Cpno = SECOND.Cno;
    

外連線

外連線與普通連線的區別

  • 普通連線操作只輸出滿足連線條件的元組
  • 外連線操作以指定表為連線主體,將主體表中不滿足連線條件的元組一併輸出
  • 左外連線
    • 列出左邊關係中所有的元組
  • 右外連線
    • 列出右邊關係中所有的元組

例子

	SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
    FROM  Student  LEFT OUT JOIN SC ON    
                 (Student.Sno=SC.Sno); 

53960143076

多表連線

兩個以上的表進行連線。

MongoDB不提供這種操作:

  • JOIN很慢
  • 多級擴充套件能力差,代價太高

例子

  1. 查詢每個學生的學號、姓名、選修的課程名及成績

      SELECT Student.Sno, Sname, Cname, Grade
      FROM   Student, SC, Course    /*多表連線*/
      WHERE Student.Sno = SC.Sno 
                   AND SC.Cno = Course.Cno;
    

巢狀查詢

  • 一個SELECT-FROM-WHERE語句稱為一個查詢塊

  • 將一個查詢塊巢狀在另一個查詢塊的WHERE子句或HAVING短語的條件中的查詢稱為巢狀查詢

  • 上層的查詢塊稱為外層查詢或父查詢

  • 下層查詢塊稱為內層查詢或子查詢

  • SQL語言允許多層巢狀查詢

    • 即一個子查詢中還可以巢狀其他子查詢
  • 子查詢的限制

    • 不能使用ORDERBY子句
    • 因為ORDER BY 結果為有序的,不滿足關係的定義,只能作為最後的生成結果

帶有IN謂詞的子查詢

  1. 查詢與“劉晨”在同一個系學習的學聲

    SELECT Sno, Sname, Sdept
          	FROM Student
          	WHERE Sdept  IN
                      (SELECT Sdept
                       FROM Student
                       WHERE Sname= ' 劉晨 ');
    /*用自身連線表示*/
    	SELECT  S1.Sno, S1.Sname,S1.Sdept
          FROM     Student S1,Student S2
          WHERE  S1.Sdept = S2.Sdept  AND
               S2.Sname = '劉晨';
    
  2. 查詢選修了課程名為“資訊系統”的學生學號和姓名

    SELECT Sno,Sname                
      	FROM    Student                         
     	WHERE Sno  IN
                 (SELECT Sno                    
                  FROM    SC                         
                  WHERE  Cno IN
                         (SELECT Cno             
                           FROM Course           
                           WHERE Cname= '資訊系統' )
                  );
    /*用連線查詢表示*/
    SELECT Sno,Sname
          FROM    Student,SC,Course
          WHERE   Student.Sno = SC.Sno  AND
                  SC.Cno = Course.Cno AND
                  Course.Cname='資訊系統';            
    

帶有比較運算子的子查詢

  • 當能確切知道內層查詢返回單值時,可用比較運算子(>,<,=,>=,<=,!=或< >)

  • 由於一個學生只可能在一個系學習,則可以用 = 代替IN :

        SELECT Sno,Sname,Sdept
        FROM    Student
        WHERE Sdept   =
                       (SELECT Sdept
                        FROM    Student
                        WHERE Sname= '劉晨');
    
  • 注意,用比較運算子取巢狀,只能SELECT一個屬性,且為數值型別。

  • 不相關子查詢

    • 子查詢的查詢條件不依賴於父查詢
    • 由裡向外逐層處理。即每個子查詢在上一級查詢處理之前求解,子查詢的結果用於建立其父查詢的查詢條件。
  • 相關子查詢

    • 子查詢的查詢條件依賴於父查詢
    • 首先取外層查詢中表的第一個元組,根據它與內層查詢相關的屬性值處理內層查詢,若WHERE子句返回值為真,則取此元組放入結果表
    • 然後再取外層表的下一個元組
    • 重複這一過程,直至外層表全部檢查完為止

例子

  1. 找出每個學生超過他選修課程平均成績的課程號

    SELECT Sno, Cno
        FROM    SC x
        WHERE Grade >= ( SELECT AVG(Grade) 
                         FROM  SC y
                         WHERE y.Sno = x.Sno );
    /*用連線查詢表示*/
    SELECT First.Sno, First.Cno
    	FROM SC First JOIN (
        	SELECT Sno, AVG(Grade) as A_Grade
            FROM SC
            GROUP BY Sno) SA
            ON First.Sno = SA.Sno
         WHERE First.Grade > SA.A_Grade
    

帶有ANY(SOME)或ALL謂詞的子查詢

使用ANY或ALL謂詞時必須同時使用比較運算

若子查詢中不是唯一的,使用ANY/ALL可以使用比較運算子

語義為:

> ANY 大於子查詢結果中的某個值

>ALL 大於子查詢結果中的所有值

>=ANY 大於等於子查詢結果中的某個值

<=ANY 小於等於子查詢結果中的某個值

=ANY 等於子查詢結果中的某個值

!=(或<>)ALL 不等於子查詢結果中的任何一個值

例子

  1. 查詢非計算機科學系中比計算機科學系任意一個學生年齡小的學生姓名和年齡

    SELECT Sname,Sage
        FROM    Student
        WHERE Sage < ANY ( SELECT  Sage
                           FROM    Student
                           WHERE Sdept= ' CS ')
         AND Sdept <> ‘CS ' ;           /*父查詢塊中的條件 */
    
    /*用聚集函式實現*/
    
    SELECT Sname,Sage
         FROM   Student
         WHERE Sage < 
                      ( SELECT MAX(Sage)
                         FROM Student
                         WHERE Sdept= 'CS ')
          AND Sdept <> ' CS ';
    
  2. 查詢非計算機科學系中比計算機科學系所有學生年齡都小的學生姓名及年齡

    SELECT Sname,Sage
        FROM Student
        WHERE Sage < ALL
                     (SELECT Sage
                       FROM Student
                       WHERE Sdept= ' CS ')
          AND Sdept <> ' CS ’;
    /*用聚集函式實現*/
    SELECT Sname,Sage
        FROM Student
        WHERE Sage < 
                   (SELECT MIN(Sage)
                    FROM Student
                    WHERE Sdept= ' CS ')
        AND Sdept <>' CS ';
    

帶有EXISTS謂詞的子查詢

EXISTS謂詞

  • 存在量詞
  • 帶有EXISTS謂詞的子查詢不返回任何資料,只產生邏輯真值“true”或邏輯假值“false”。
  • 若內層查詢結果非空,則外層的WHERE子句返回真值
  • 若內層查詢結果為空,則外層的WHERE子句返回假值
  • 由EXISTS引出的子查詢,其目標列表達式通常都用* ,因為帶EXISTS的子查詢只返回真值或假值,給出列名無實際意義。

例子

  1. 查詢所有選修了1號課程的學生姓名。

    思路

    • 本查詢涉及StudentSC關係
    • Student中依次取每個元組的Sno值,用此值去檢查SC
    • SC中存在這樣的元組,其Sno值等於此Student.Sno值,並且其Cno=‘1’,則取此Student.Sname送入結果表
    SELECT Sname
         FROM Student
         WHERE EXISTS
                       (SELECT *
                        FROM SC
                        WHERE Sno=Student.Sno AND Cno= ' 1 ');
    
  2. 查詢沒有選修1號課程的學生姓名。

    SELECT Sname
         FROM     Student
         WHERE NOT EXISTS
                       (SELECT *
                        FROM SC
                        WHERE Sno = Student.Sno AND 	  Cno='1');
    

難點

  • 不同形式的查詢間的替換

    • 一些帶EXISTS或NOT EXISTS謂詞的子查詢不能被其他形式的子查詢等價替換

    • 所有帶IN謂詞、比較運算子、ANY和ALL謂詞的子查詢都能用帶EXISTS謂詞的子查詢等價替換

    • 查詢與“劉晨”在同一個系學習的學生

      • 可以用帶EXISTS謂詞的子查詢替換

      • SELECT Sno,Sname,Sdept
             FROM Student S1
              WHERE EXISTS
                         (SELECT *
                             FROM Student S2
                             WHERE S2.Sdept = S1.Sdept AND
                                   S2.Sname = '劉晨');
        
  • 用EXISTS/NOT EXISTS實現全稱量詞(難點)

    • 查詢選修了全部課程的學生姓名

      • 不存在一門課,這個學生沒有選

      • SELECT Sname
                FROM Student
                WHERE NOT EXISTS
                              (SELECT *
                                FROM Course
                                WHERE NOT EXISTS
                                              (SELECT *
                                               FROM SC
                                               WHERE Sno= Student.Sno
                                                     AND Cno= Course.Cno
                                              )
                               );