SQL語句專題

/*
概述:前段時,不就間看了一個牛人的博客,來檢查一下SQL的能力的文章,剛開始覺得沒什麼幾張
簡單表嗎?  其實最後看了看就從這麼4張簡單表中竟然能挖掘出那麼多的SQL語句。而且有些SQL實話說
還真有點不好搞。那篇博客是基於SQL SERVER的,我從新來用ORACLE實現一次,且我會盡量想多種實現方式
去寫,需要的的話,可以比較他們之間的性能差異。

說明:本節只是針對查詢,表我已經建好了,數據也準備好了。只需要執行以下語句就好了。
然後就可以把自己的SQL運行一下查看結果和我的進行比較。不要刪除和修改我給出的初始
數據。

*/

--注意:大寫爲ORACLE關鍵字

CREATE TABLE test_z_student(
  s#  VARCHAR2(20),
  sname VARCHAR2(20),
  sage  int,
  ssex VARCHAR2(20)
);

CREATE TABLE test_z_course(
  c# VARCHAR2(20),
  cname VARCHAR2(20),
  t# VARCHAR2(20)
);


CREATE TABLE test_z_sc(
  s# VARCHAR2(20), --學生ID
  c# VARCHAR2(20),  --課程ID
  score int   --分數
);

CREATE TABLE test_z_teacher(
  t# VARCHAR2(20),  --教師D
  tname VARCHAR2(20) --教師姓名
);


---向學生表中插入數據
INSERT INTO test_z_student VALUES('stu001','stu001-name',15,'F');
INSERT INTO test_z_student VALUES('stu002','stu002-name',21,'M');
INSERT INTO test_z_student VALUES('stu003','stu003-name',20,'F');
INSERT INTO test_z_student VALUES('stu004','stu004-name',30,'M');
INSERT INTO test_z_student VALUES('stu005','stu005-name',18,'M');
INSERT INTO test_z_student VALUES('stu006','stu006-name',25,'F');
INSERT INTO test_z_student VALUES('stu007','stu007-name',26,'F');
INSERT INTO test_z_student VALUES('stu008','stu008-name',32,'M');
INSERT INTO test_z_student VALUES('stu009','stu009-name',24,'M');
INSERT INTO test_z_student VALUES('stu010','stuss-name',24,'M');

--向教師表中插入數據
INSERT INTO test_z_teacher VALUES('tcha-1','tcha-1-name');
INSERT INTO test_z_teacher VALUES('tcha-2','tcha-2-name');
INSERT INTO test_z_teacher VALUES('tcha-3','tcha-3-name');
INSERT INTO test_z_teacher VALUES('tchb-1','tchb-1-name');
INSERT INTO test_z_teacher VALUES('tchb-2','tchb-2-name');
INSERT INTO test_z_teacher VALUES('tchc-1','tchc-1-name');
INSERT INTO test_z_teacher VALUES('tchd-1','tchd-1-name');
INSERT INTO test_z_teacher VALUES('tchd-2','tchd-2-name');

--向課程表中插入數據
INSERT INTO test_z_course VALUES('c1','c1-name','tcha-1');
INSERT INTO test_z_course VALUES('c2','c2-name','tcha-2');
INSERT INTO test_z_course VALUES('c3','c3-name','tcha-3');
INSERT INTO test_z_course VALUES('c4','c4-name','tchb-1');
INSERT INTO test_z_course VALUES('c5','c5-name','tchb-2');
INSERT INTO test_z_course VALUES('c6','c6-name','tchc-1');
INSERT INTO test_z_course VALUES('c7','c7-name','tchd-1');
INSERT INTO test_z_course VALUES('c8','c8-name','tchd-2');
INSERT INTO test_z_course VALUES('c9','c9-name','tchb-1');


--向學生課程中間表中插入數據  
INSERT INTO test_z_sc VALUES('stu001','c1',65);
INSERT INTO test_z_sc VALUES('stu001','c2',50);
INSERT INTO test_z_sc VALUES('stu001','c3',86);
INSERT INTO test_z_sc VALUES('stu001','c4',72);
INSERT INTO test_z_sc VALUES('stu001','c5',12);
INSERT INTO test_z_sc VALUES('stu001','c6',45);
INSERT INTO test_z_sc VALUES('stu001','c7',74);
INSERT INTO test_z_sc VALUES('stu001','c8',47);
INSERT INTO test_z_sc VALUES('stu001','c9',45);

INSERT INTO test_z_sc VALUES('stu002','c1',22);
INSERT INTO test_z_sc VALUES('stu002','c2',50);
INSERT INTO test_z_sc VALUES('stu002','c3',32);
INSERT INTO test_z_sc VALUES('stu002','c4',89);
INSERT INTO test_z_sc VALUES('stu002','c5',78);
INSERT INTO test_z_sc VALUES('stu002','c6',54);
INSERT INTO test_z_sc VALUES('stu002','c7',13);
INSERT INTO test_z_sc VALUES('stu002','c8',65);


INSERT INTO test_z_sc VALUES('stu003','c1',85);
INSERT INTO test_z_sc VALUES('stu003','c2',78);
INSERT INTO test_z_sc VALUES('stu003','c3',69);
INSERT INTO test_z_sc VALUES('stu003','c4',63);
INSERT INTO test_z_sc VALUES('stu003','c5',45);
INSERT INTO test_z_sc VALUES('stu003','c6',85);
INSERT INTO test_z_sc VALUES('stu003','c7',96);
INSERT INTO test_z_sc VALUES('stu003','c8',21);
INSERT INTO test_z_sc VALUES('stu003','c9',37);

INSERT INTO test_z_sc VALUES('stu004','c1',68);
INSERT INTO test_z_sc VALUES('stu004','c2',92);
INSERT INTO test_z_sc VALUES('stu004','c3',64);
INSERT INTO test_z_sc VALUES('stu004','c4',31);
INSERT INTO test_z_sc VALUES('stu004','c5',78);
INSERT INTO test_z_sc VALUES('stu004','c6',55);
INSERT INTO test_z_sc VALUES('stu004','c7',96);
INSERT INTO test_z_sc VALUES('stu004','c8',35);


INSERT INTO test_z_sc VALUES('stu005','c1',36);
INSERT INTO test_z_sc VALUES('stu005','c2',93);
INSERT INTO test_z_sc VALUES('stu005','c3',66);
INSERT INTO test_z_sc VALUES('stu005','c4',87);
INSERT INTO test_z_sc VALUES('stu005','c5',23);
INSERT INTO test_z_sc VALUES('stu005','c6',52);
INSERT INTO test_z_sc VALUES('stu005','c7',85);
INSERT INTO test_z_sc VALUES('stu005','c8',47);


INSERT INTO test_z_sc VALUES('stu006','c1',85);
INSERT INTO test_z_sc VALUES('stu006','c2',58);
INSERT INTO test_z_sc VALUES('stu006','c3',47);
INSERT INTO test_z_sc VALUES('stu006','c4',47);
INSERT INTO test_z_sc VALUES('stu006','c5',56);
INSERT INTO test_z_sc VALUES('stu006','c6',25);

INSERT INTO test_z_sc VALUES('stu007','c1',33);
INSERT INTO test_z_sc VALUES('stu007','c2',33);
INSERT INTO test_z_sc VALUES('stu007','c3',96);

INSERT INTO test_z_sc VALUES('stu008','c1',88);
INSERT INTO test_z_sc VALUES('stu008','c2',77);
INSERT INTO test_z_sc VALUES('stu008','c3',55);
INSERT INTO test_z_sc VALUES('stu008','c4',22);

INSERT INTO test_z_sc VALUES('stu009','c1',55);
INSERT INTO test_z_sc VALUES('stu009','c7',55);
INSERT INTO test_z_sc VALUES('stu009','c8',22);

INSERT INTO test_z_sc VALUES('stu010','c1',23);
commit;


-------------------------言歸正傳,開始吧! ------------------------

---------(1) 查詢課程c1 與 c2 分數相等的學生。
 SELECT a.s#
          FROM (SELECT FROM test_z_sc t1 WHERE t1.c# = 'c1') a
          JOIN (SELECT FROM test_z_sc t1 WHERE t1.c# = 'c2') b
            ON (a.s# = b.s#)
         WHERE a.score = b.score

SELECT DISTINCT t.*
  FROM test_z_student t
  JOIN (
          SELECT a.s#
          FROM (SELECT FROM test_z_sc t1 WHERE t1.c# = 'c1') a
          JOIN (SELECT * FROM test_z_sc t1 WHERE t1.c# = 'c2') b
            ON (a.s# = b.s#)
         WHERE a.score = b.score
       ) r
    ON (t.s# = r.s#);
    
---------(2) 查詢平均成績大於60分的同學的學號和平均成績;

SELECT sc.s#, AVG(sc.score)
  FROM test_z_sc sc
 GROUP BY sc.s#
HAVING AVG(sc.score) > 60;

SELECT t.*,r.avg_socre FROM test_z_student t 
JOIN (
SELECT sc.s#, AVG(sc.score) avg_socre
  FROM test_z_sc sc
 GROUP BY sc.s#
HAVING AVG(sc.score) > 60
) r ON(r.s# = t.s#)

---------(3) 查詢所有同學的學號、姓名、選課數、總成績;
SELECT sc.s#, COUNT(sc.c#),SUM(sc.score) FROM test_z_sc sc GROUP BY sc.s#;
  --方法1
SELECT  t.* ,r.c_c,r.s_c FROM test_z_student t
  JOIN (
    SELECT sc.s#, COUNT(sc.c#) c_c,SUM(sc.score) s_c FROM test_z_sc sc GROUP BY sc.s#
  ) r ON (t.s# = r.s#) ORDER BY t.s#; 
  
  --方法2
/*
思考:LEFT JOIN 與 RIGHT JOIN 究竟用在什麼時候,遇到怎樣的問題的時候會考慮到。如下:
LEFT JOIN:當需要這邊表的所有行數據,同時還需要額外的列額時候可以考慮。同理 RIGHT JOIN一樣。
*/  
SELECT t.s#,t.sname,COUNT(sc.c#),SUM(sc.score)
 FROM test_z_student t LEFT  JOIN test_z_sc sc ON (t.s# = sc.s#)
 GROUP BY t.s#,t.sname ORDER BY t.s#;


---------(4) 查詢姓名以“tcha”開頭的老師的個數;
  --方法1
SELECT SUM(COUNT(t.tname)) 個數
  FROM test_z_teacher t
 WHERE t.tname LIKE 'tcha%'
 GROUP BY t.tname;
  --方法2 
SELECT COUNT(DISTINCT(t.tname)) 個數
  FROM test_z_teacher t
 WHERE t.tname LIKE 'tcha%';

---------(5) 查詢沒學過“tchd-2-name”老師課的同學的學號、姓名;

  --方法1
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# NOT IN
       (SELECT sc.s#
          FROM test_z_sc sc
         WHERE sc.c# IN (SELECT tc.c#
                           FROM test_z_teacher tec
                           JOIN test_z_course tc
                             ON (tec.t# = tc.t#)
                          WHERE tec.tname = 'tchd-2-name'));
  --方法2
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# NOT IN (SELECT sc.s#
                        FROM test_z_sc sc
                        JOIN test_z_course tc
                          ON (sc.c# = tc.c#)
                        JOIN test_z_teacher tec
                          ON (tec.t# = tc.t#)
                       WHERE tec.tname = 'tchd-2-name');
                       
---------(6) 查詢學過“c1”並且也學過編號“c6”課程的同學的學號、姓名;    
  --方法1  
SELECT stu.*
  FROM test_z_student stu
  JOIN (SELECT sc1.s#
          FROM test_z_sc sc1
          JOIN test_z_sc sc2
            ON (sc1.s# = sc2.s#) WHERE sc1.c# = 'c1'
           AND sc2.c# = 'c6') r
    ON (stu.s# = r.s#);
  --方法2    
SELECT stu.*  FROM test_z_student stu 
  JOIN test_z_sc sc ON (stu.s# = sc.s#)
  WHERE sc.c# = 'c1' 
  AND EXISTS (
   SELECT * FROM test_z_sc sc2 
     WHERE sc2.s# = sc.s# 
     AND sc2.c# = 'c6'
  );

---------(7) 查詢學過“tchb-1-name”老師所教的所有課的同學的學號、姓名;
/*
思路分析: 先查找出學過“tchb-1-name”老師所教的任何一門課程的學生號,這時如果學了多門該老師
的課程的話,同一個學生會有多條記錄,這就意味着每一條記錄就是學的該老師的一門課程。然後再在
這個結果集中分組,在上一個結果集中的一個學生的重複記錄數目等於該老師所教課程的數目就OK。
*/
  
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# IN
       (SELECT sc.s#
          FROM test_z_sc sc
          JOIN test_z_course tc
            ON (sc.c# = tc.c#)
          JOIN test_z_teacher tec
            ON (tc.t# = tec.t#)
         WHERE tec.tname = 'tchb-1-name'
         GROUP BY sc.s#
        HAVING COUNT(sc.c#) = (SELECT COUNT(tc2.c#)
                                FROM test_z_course tc2
                                JOIN test_z_teacher tec2
                                  ON (tc2.t# = tec2.t#)
                               WHERE tec2.tname = 'tchb-1-name'));
                        
                        
---------(8) 查詢課程編號“c2”的成績比課程編號“c1”課程低的所有同學的學號、姓名;
--方法1
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# IN
       (SELECT r_1.s#
          FROM (SELECT sc.s#, sc.score FROM test_z_sc sc WHERE sc.c# = 'c2') r_1
          JOIN (SELECT sc.s#, sc.score FROM test_z_sc sc WHERE sc.c# = 'c1') r_2
            ON (r_1.s# = r_2.s#)
         WHERE r_1.score < r_2.score);
--方法2         
SELECT stu.*
  FROM test_z_student stu
  JOIN (SELECT r_1.s#
          FROM (SELECT sc.s#, sc.score FROM test_z_sc sc WHERE sc.c# = 'c2') r_1
          JOIN (SELECT sc.s#, sc.score FROM test_z_sc sc WHERE sc.c# = 'c1') r_2
          ON (r_1.s# = r_2.s#)
         WHERE r_1.score < r_2.score) tr
    ON (tr.s# = stu.s#);
--方法3
/* 方法3是將向比較的列合併到同一個結果集中。如下:
SELECT stu.s#,
       stu.sname,
       score,
       (SELECT score
          FROM test_z_sc sc2
         WHERE sc2.s# = stu.s#
           AND sc2.c# = 'c2') score2
  FROM test_z_student stu, test_z_sc sc
 WHERE stu.s# = sc.s#
   AND sc.c# = 'c1'
 */
SELECT s#, sname
  FROM (SELECT stu.s#,
               stu.sname,
               score,
               (SELECT score
                  FROM test_z_sc sc2
                 WHERE sc2.s# = stu.s#
                   AND sc2.c# = 'c2') score2
          FROM test_z_student stu, test_z_sc sc
         WHERE stu.s# = sc.s#
           AND sc.c# = 'c1') s_2
 WHERE score2 < score
  
---------(9) 查詢所有課程成績小於60分的同學的學號、姓名;
/*
思考:有些問題,得用“反向思考”去解決。如本例,只要有一門課程大於60分的同學就不符合要求,即可排除。
要是不用反向解決問題的辦法,否則本例這個問題還很不好解決。
*/
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# NOT IN (SELECT stu2.s#
                        FROM test_z_student stu2
                        JOIN test_z_sc sc
                          ON (stu2.s# = sc.s#)
                       WHERE sc.score > 60);
  
---------(10) 查詢沒有學全所有課的同學的學號、姓名;
/*
思路:中間表按照學生分組後,每個學生分組的COUNT數目小於課程表中的總記錄條數即可。
*/
--方法1.採用的 IN 方式
SELECT stu.*
  FROM test_z_student stu
 WHERE stu.s# IN
       (
        
        SELECT sc.s#
          FROM test_z_sc sc
         GROUP BY sc.s#
        HAVING COUNT(sc.c#) < (SELECT COUNT(tc.c#) FROM test_z_course tc));
--方法2. 採用 JOIN 方式。
SELECT stu.*
  FROM test_z_student stu
  JOIN (SELECT sc.s#
          FROM test_z_sc sc
         GROUP BY sc.s#
        HAVING COUNT(sc.c#) < (SELECT COUNT(tc.c#) FROM test_z_course tc)) r
    ON (r.s# = stu.s#);
--方法3
SELECT stu.s#, stu.sname
  FROM test_z_student stu
  JOIN test_z_sc sc
    ON (stu.s# = sc.s#)
 GROUP BY stu.s#, stu.sname
HAVING COUNT(sc.c#) < (SELECT COUNT(tc.c#) FROM test_z_course tc)

---------(11) 查詢至少有一門課與學號爲“stu009”的同學所學相同的同學的學號和姓名;

SELECT DISTINCT stu.*
  FROM test_z_student stu
  JOIN test_z_sc sc
    ON (stu.s# = sc.s#)
 WHERE sc.c# IN (SELECT sc2.c# FROM test_z_sc sc2 WHERE sc2.s# = 'stu009');
  
---------(12) 查詢至少學過學號爲“stu009”同學所學的任意一門課的其他同學學號和姓名
/*
這句話表達的意思和上一個相同
*/
SELECT DISTINCT stu.*
  FROM test_z_student stu
  JOIN test_z_sc sc
    ON (stu.s# = sc.s#)
 WHERE sc.c# IN (SELECT sc2.c# FROM test_z_sc sc2 WHERE sc2.s# = 'stu009');

---------(13) 把“test_z_sc_brk”表中“tchb-1-name”老師教的課的成績都更改爲此課程的平均成績;
/*未解決*/

---------(14) 查詢至少包含“stu002”號的同學所學習的全部課程的同學的學號和姓名;
SELECT stu.s#, stu.sname, COUNT(*)
  FROM test_z_student stu
  JOIN test_z_sc sc
    ON (sc.s# = stu.s#)
 WHERE sc.c# IN (SELECT sc2.c# FROM test_z_sc sc2 WHERE sc2.s# = 'stu002')
 GROUP BY stu.s#, stu.sname
HAVING COUNT(*) = (SELECT COUNT(*)
                     FROM test_z_sc sc3
                    WHERE sc3.s# = 'stu002');

---------(15) 查詢和“stu002”號的同學所學的課程完全相同(不能多也不能少)的同學學號和姓名;
SELECT stu.s#, stu.sname, COUNT(*)
  FROM test_z_student stu
  JOIN test_z_sc sc
    ON (sc.s# = stu.s#)
 WHERE sc.c# IN (SELECT sc2.c# FROM test_z_sc sc2 WHERE sc2.s# = 'stu002')
   AND sc.s# NOT IN
       (SELECT sc.s#
          FROM test_z_sc sc
         GROUP BY sc.s#
        HAVING COUNT(sc.c#) > (SELECT COUNT(*)
                                FROM test_z_sc sc3
                               WHERE sc3.s# = 'stu002'))
 GROUP BY stu.s#, stu.sname
HAVING COUNT(*) = (SELECT COUNT(*)
                     FROM test_z_sc sc3
                    WHERE sc3.s# = 'stu002');

---------(16) 刪除學習“tchb-1-name”老師課的SC表記錄;
/*未解決*/

---------(17) 查詢如下描述的結果集
/*
按平均成績從高到低顯示所有學生的“c1_name”、“c2_name”、“c3_name”三門的課程成績,
本例中有效課程數指的是學生學的所有課程,有效平均分是學生學的所有課程的成績除以該學生所學的課程數。
按如下形式顯示: 
學生ID,c1_name,c2_name,c1_name,有效課程數,有效平均分
*/

--方法1
SELECT sc.s# 學生ID,
       sc1.score c1_name,
       sc2.score c2_name,
       sc3.score c3_name,
       COUNT(*) 有效課程數,
       AVG(sc.score) 有效平均分
  FROM test_z_sc sc
  LEFT JOIN test_z_sc sc1 ON (sc.s# = sc1.s# AND sc1.c# = 'c1')
  LEFT JOIN test_z_sc sc2 ON (sc.s# = sc2.s# AND sc2.c# = 'c2')
  LEFT JOIN test_z_sc sc3 ON (sc.s# = sc3.s# AND sc3.c# = 'c3')
 GROUP BY sc.s#, sc1.score, sc2.score, sc3.score
 ORDER BY AVG(sc.score);
 
--方法2  
SELECT sc.s# 學生ID,
       NVL((SELECT sc1.score
             FROM test_z_sc sc1
            WHERE sc.s# = sc1.s#
              AND sc1.c# = 'c1'),
           0) c1_name,
       NVL((SELECT sc2.score
             FROM test_z_sc sc2
            WHERE sc.s# = sc2.s#
              AND sc2.c# = 'c2'),
           0) c2_name,
       NVL((SELECT sc3.score
             FROM test_z_sc sc3
            WHERE sc.s# = sc3.s#
              AND sc3.c# = 'c3'),
           0) c3_name,
       COUNT(*) 有效課程數,
       AVG(sc.score) 有效平均分
  FROM test_z_sc sc
 GROUP BY sc.s#
 ORDER BY AVG(sc.score);
 
--上述SQL中方法1的效率明顯比方法2的差很多,思考原因在什麼地方?

---------(18) 查詢各科成績最高和最低的分:以如下形式顯示:課程ID,最高分,最低分

SELECT sc.c# 課程ID, MAX(sc.score) 最高分, MIN(sc.score) 最低分
  FROM test_z_sc sc
 GROUP BY sc.c#
 ORDER BY sc.c#;
 
---------(19) 按各科平均成績從低到高和大於60分的人數。顯示格式爲: 課程ID,平均成績,大於60分的人數
  
SELECT sc.c#,
       TRUNC(NVL(AVG(sc.score), 0),2) 平均成績,
       SUM(CASE
             WHEN NVL(sc.score, 0) >= 60 THEN
              1
             ELSE
              0
           END) 大於60分的人數
  FROM test_z_sc sc
 GROUP BY sc.c# ;
 
---------(20) 查詢各科平均成績和及格(大於60分)率的百分數,並且按照及格率進行升序排序 

SELECT sc.c#,
       TRUNC(NVL(AVG(sc.score), 0), 2) 平均成績,
       (100 * TRUNC(SUM(CASE
                          WHEN NVL(sc.score, 0) >= 60 THEN
                           1
                          ELSE
                           0
                        END) / COUNT(*),
                    4)) || '%' AS 大於60分的比率
  FROM test_z_sc sc
 GROUP BY sc.c#
 ORDER BY (SUM(CASE
                 WHEN NVL(sc.score, 0) >= 60 THEN
                  1
                 ELSE
                  0
               END) / COUNT(*));
               
---------(21) 按要求查詢下列結果集
/*
查詢如下課程平均成績和及格率的百分數(用"1行"顯示查詢結果集);顯示格式如下:

c1_name平均成績  |c1_name及格率 |c2_name平均成績 |c2_name及格率|c3_name平均成績|c3_name及格率 

*/               

SELECT SUM(CASE
             WHEN sc.c# = 'c1' THEN
              NVL(sc.score, 0)
             ELSE
              0
           END) / (SUM(CASE
                         WHEN sc.c# = 'c1' THEN
                          1
                         ELSE
                          0
                       END)) AS c1_name平均成績,
       
       TRUNC(NVL(100 * SUM(CASE
                             WHEN (sc.c# = 'c1' AND NVL(sc.score, 0) >= 60) THEN
                              1
                             ELSE
                              0
                           END) / (SUM(CASE
                                         WHEN sc.c# = 'c1' THEN
                                          1
                                         ELSE
                                          0
                                       END)),
                 0),
             4) || '%' AS c1_name及格率,
       SUM(CASE
             WHEN sc.c# = 'c2' THEN
              NVL(sc.score, 0)
             ELSE
              0
           END) / (SUM(CASE
                         WHEN sc.c# = 'c2' THEN
                          1
                         ELSE
                          0
                       END)) AS c2_name平均成績,
       
       TRUNC(NVL(100 * SUM(CASE
                             WHEN (sc.c# = 'c2' AND NVL(sc.score, 0) >= 60) THEN
                              1
                             ELSE
                              0
                           END) / (SUM(CASE
                                         WHEN sc.c# = 'c2' THEN
                                          1
                                         ELSE
                                          0
                                       END)),
                 0),
             4) || '%' AS c2_name及格率,
       SUM(CASE
             WHEN sc.c# = 'c3' THEN
              NVL(sc.score, 0)
             ELSE
              0
           END) / (SUM(CASE
                         WHEN sc.c# = 'c3' THEN
                          1
                         ELSE
                          0
                       END)) AS c3_name平均成績,
       
       TRUNC(NVL(100 * SUM(CASE
                             WHEN (sc.c# = 'c3' AND NVL(sc.score, 0) >= 60) THEN
                              1
                             ELSE
                              0
                           END) / (SUM(CASE
                                         WHEN sc.c# = 'c3' THEN
                                          1
                                         ELSE
                                          0
                                       END)),
                 0),
             4) || '%' AS c3_name及格率
  FROM test_z_sc sc;

/*
思考:好好領悟CASE的執行步驟,特別是當它與聚合函數結合起來使用的時候。
*/


---------(22) 查詢所有的老師所教的不同課程平均分從高到低顯示。顯示格式如下:
--  教師ID,教師姓名,課程ID,課程名稱,平均成績

SELECT MAX(tec.t#) AS 教師ID,
       MAX(tec.tname) AS 教師姓名,
       sc.c# AS 課程ID,
       MAX(tc.cname) AS 課程名稱,
       NVL(AVG(sc.score), 0) AS 平均成績
  FROM test_z_sc sc
  JOIN test_z_course tc ON (sc.c# = tc.c#)
  JOIN test_z_teacher tec ON (tc.t# = tec.t#)
 GROUP BY sc.c#
 ORDER BY NVL(AVG(sc.score), 0);

/*
思考:
上述這個SQL有個技巧,當需要得到不在 GROUP BY 列中的字段時,可以考慮使用聚合函數。如本例的:
       MAX(tec.t#) AS 教師ID,
       MAX(tec.tname) AS 教師姓名,
       MAX(tc.cname) AS 課程名稱,
*/


---------(23) 查詢如下需求的結果集
/*
查詢 c1_name(c1),c2_name(c2),c3_name (c3)課程成績第 3 名到第 6 名的學生成績單:
結果集格式如下:
[學生ID],[學生姓名],c1_name成績,c2_name成績,UML,c3_name成績
*/
/*
提示: Oracle查詢第M到第N條數據公式:
SELECT *
  FROM (SELECT *, ROWNUM AS con
          FROM (SELECT * FROM TABLE ORDER BY colum)
         WHERE ROWNUM <= N)
 WHERE con >= M;
 
如在本例中查詢test_z_sc表的第2條到第4條數據,如下:
SELECT *
  FROM (SELECT r.*, ROWNUM AS num
          FROM (SELECT * FROM test_z_sc sc) r
         WHERE ROWNUM <= 4)
 WHERE num >= 2;
*/
--方法1
--第一步:按照要查詢的課程排序。沒有取學生姓名,關聯一下就可以了。
SELECT sc.s# AS 學生ID,
      NVL( ( SELECT sc1.score FROM test_z_sc sc1 WHERE sc.s# = sc1.s# AND sc1.c#='c1' ),0) AS c1_name成績,
      NVL( ( SELECT sc2.score FROM test_z_sc sc2 WHERE sc.s# = sc2.s# AND sc2.c#='c2'  ),0) AS c2_name成績,
      NVL( ( SELECT sc3.score FROM test_z_sc sc3 WHERE sc.s# = sc3.s# AND sc3.c#='c3'  ),0) AS c3_name成績
  FROM test_z_sc sc
  GROUP BY sc.s#
  ORDER BY c1_name成績,c2_name成績,c3_name成績;
--第二步,在第一步中從排序的結果集中取出第3條到第6條數據  
SELECT *
  FROM (SELECT t.*, ROWNUM as num
          FROM (
             SELECT sc.s# AS 學生ID,
                    NVL( ( SELECT sc1.score FROM test_z_sc sc1 WHERE sc.s# = sc1.s# AND sc1.c#='c1' ),0) AS c1_name成績,
                    NVL( ( SELECT sc2.score FROM test_z_sc sc2 WHERE sc.s# = sc2.s# AND sc2.c#='c2'  ),0) AS c2_name成績,
                    NVL( ( SELECT sc3.score FROM test_z_sc sc3 WHERE sc.s# = sc3.s# AND sc3.c#='c3'  ),0) AS c3_name成績
               FROM test_z_sc sc
               GROUP BY sc.s#
               ORDER BY c1_name成績,c2_name成績,c3_name成績
               ) t
               WHERE ROWNUM <= 6) r
 WHERE r.num >= 3;
 
--方法2.可以使用 LEFT JOIN的方式。
--第一步,查詢需要的課程成績並排序。
SELECT  DISTINCT stu.s# AS 學生ID,
        stu.sname AS 學生姓名,
        NVL(sc1.score,0) AS c1_name成績,
        NVL(sc2.score,0) AS c2_name成績,
        NVL(sc3.score,0) AS c3_name成績
  FROM test_z_sc sc
  JOIN test_z_student stu ON( sc.s# = stu.s# )
  LEFT JOIN test_z_sc sc1 ON( sc.s# = sc1.s# AND sc1.c# = 'c1' )
  LEFT JOIN test_z_sc sc2 ON( sc.s# = sc2.s# AND sc2.c# = 'c2' )
  LEFT JOIN test_z_sc sc3 ON( sc.s# = sc3.s# AND sc3.c# = 'c3' )
  ORDER BY c1_name成績,c2_name成績,c3_name成績
--第二步,從第一步的結果集中取出第3到第6條數據。這和第一種方法的第二步是一樣的。
SELECT *
  FROM (SELECT t.*, ROWNUM as num
          FROM (
             SELECT  DISTINCT stu.s# AS 學生ID,
                     stu.sname AS 學生姓名,
                     NVL(sc1.score,0) AS c1_name成績,
                     NVL(sc2.score,0) AS c2_name成績,
                     NVL(sc3.score,0) AS c3_name成績
                FROM test_z_sc sc
                JOIN test_z_student stu ON( sc.s# = stu.s# )
                LEFT JOIN test_z_sc sc1 ON( sc.s# = sc1.s# AND sc1.c# = 'c1' )
                LEFT JOIN test_z_sc sc2 ON( sc.s# = sc2.s# AND sc2.c# = 'c2' )
                LEFT JOIN test_z_sc sc3 ON( sc.s# = sc3.s# AND sc3.c# = 'c3' )
                ORDER BY c1_name成績,c2_name成績,c3_name成績
               ) t
         WHERE ROWNUM <= 6) r
 WHERE r.num >= 3;  


---------(24) 統計各科成績在各分數段人數。結果集顯示格式爲: 課程ID,課程名稱,[90-100],[75-85],[60-70],[ <60]

SELECT  sc.c#,tc.cname,
        SUM( CASE WHEN NVL(sc.score,0) >=90 AND NVL(sc.score,0) <=100 THEN 
              1
             ELSE
              0
        END ) AS 九十到一百,
        SUM( CASE WHEN NVL(sc.score,0) >=75 AND NVL(sc.score,0) <=85 THEN 
              1
             ELSE 
              0
        END ) AS 七十五到八十五,
        SUM( CASE WHEN NVL(sc.score,0) >=60 AND NVL(sc.score,0) <=70 THEN 
              1
             ELSE 
              0                
        END ) AS 六十到七十,
        SUM( CASE WHEN NVL(sc.score,0) <60 THEN 
              1
             ELSE 
              0           
        END ) AS 小於60分的人數
  FROM test_z_sc sc 
  JOIN test_z_course tc ON(sc.c# = tc.c# )
  GROUP  BY  sc.c#,tc.cname

---------(25) 查詢學生平均成績及其名次
--方法1。使用ORACLE的位列ROWNUM
SELECT t.*, ROWNUM AS 名次
  FROM (SELECT TRUNC(AVG(sc.score), 2) AS 平均成績
          FROM test_z_sc sc
         GROUP BY sc.s#
         ORDER BY 平均成績 DESC) t ;
         
--方法2.直接硬性的排序(排名次)。使用兩個一模一樣的結果集來排序。不過這種效率很低。但思考方式值得借鑑。
/*
提示:以其中一個結果集爲參照,用另一結果集的每一個元素和參照結果集的每個元素做對比。
  就是下面查詢中的T1結果集與T2結果集。
*/
SELECT 1 +(SELECT COUNT(distinct 平均成績)
              FROM (SELECT sc.s#, TRUNC(AVG(sc.score),2) AS 平均成績
                      FROM test_z_sc sc
                     GROUP BY sc.s#) T1
             WHERE 平均成績 > T2.平均成績) as 名次,
       t2.S# as 學生學號,
       t2.平均成績
  FROM (SELECT sc2.s#, TRUNC(AVG(sc2.score),2) 平均成績
          FROM test_z_sc sc2
         GROUP BY sc2.s#) T2
 ORDER BY 平均成績 desc;
               
---------(26) 查詢各科成績前三名的記錄(當出現相同分數的時候分別考慮不同的排名方式);
--顯示方式爲: 課程名稱   學生ID   分數   排名
/*
這裏會使用到ORACLE的排名函數。大致介紹如下:
rank,dense_rank,row_number,以及分組排名partition

rank:排名會出現並列第n名,它之後的會跳過空出的名次,例如:1,2,2,4

dense_rank:排名會出現並列第n名,它之後的名次爲n+1,例如:1,2,2,3

row_number:排名採用唯一序號連續值,例如1,2,3,4

partition:將排名限制到某一分組
*/

--方法1.row_number() 排名方式
select t1.cname 課程名稱, temp.s# 學生ID, temp.score 分數,temp.排名
  from (select t.c#,
               t.s#,
               row_number() over(partition by t.c# order by t.score desc) 排名,
               t.score
          from test_z_sc t) temp,
       test_z_course t1
 where temp.c# = t1.c#
   and temp.排名 < 4;
   
--方法2.RANK() 排名方式  
select t1.cname 課程名稱, temp.s# 學生ID, temp.score 分數,temp.排名
  from (select t.c#,
               t.s#,
               RANK() over(partition by t.c# order by t.score desc) 排名,
               t.score
          from test_z_sc t) temp,
       test_z_course t1
 where temp.c# = t1.c#
   and temp.排名 < 4  ; 
--方法3.dense_rank() 排名方式       
select t1.cname 課程名稱, temp.s# 學生ID, temp.score 分數,temp.排名
  from (select t.c#,
               t.s#,
               dense_rank() over(partition by t.c# order by t.score desc) 排名,
               t.score
          from test_z_sc t) temp,
       test_z_course t1
 where temp.c# = t1.c#
   and temp.排名 < 4  ;

---------(27) 以總分排名查詢所有同學的名次(當出現相同總數的時候分別考慮不同的排名方式):
--顯示方式爲: 學生ID   總分   排名

--當出現總分相同的時候可以參考上面的實現方式
--方法1.用ORACLE的排名函數
SELECT r.s#  學生ID  ,
       r.total  總分 , 
       RANK() OVER( ORDER BY r.total DESC ) 排名
  FROM (
    SELECT sc.s#,SUM(sc.score) total 
    FROM test_z_sc sc 
    GROUP BY sc.s#
  ) r ;
--方法2.用ROWNUM的方式。但這種方式不能考慮多種並列排序方式。
SELECT r.*, ROWNUM 排名
  FROM (SELECT sc.s# 學生ID, SUM(sc.score) 總分
          FROM test_z_sc sc
         GROUP BY sc.s#
         ORDER BY 總分 DESC) r
  
---------(28) 查詢每門課程被選修的學生數 
SELECT  sc.c# 課程ID , COUNT(sc.s#)  學生數
  FROM test_z_sc sc  
  GROUP BY sc.c# ;

---------(29) 查詢出只學了2門課程的全部學生的學號和姓名 
SELECT sc.s#,
       stu.sname 
  FROM test_z_sc sc 
  JOIN test_z_student stu ON(stu.s# = sc.s#)
  GROUP  BY sc.s#,stu.sname
  HAVING COUNT(sc.s#) = 2 ;

---------(30) 查詢男生、女生人數 。顯示格式爲: 性別 , 人數
--方法1。CASE用法
SELECT CASE
         WHEN UPPER(stu.ssex) = 'M' THEN
          '男'
         WHEN UPPER(stu.ssex) = 'F' THEN
          '女'
          ELSE
          '未知'
       END 性別,
       COUNT(stu.ssex) 人數
  FROM test_z_student stu
 GROUP BY stu.ssex;
--方法2。DECODE函數用法
/*
DECODE(UPPER(stu.ssex),'M','男','F','女','未知') 解釋:
選定標準的值爲UPPER(stu.ssex)。當UPPER(stu.ssex)爲“M”的時候返回“男”,
當UPPER(stu.ssex)爲“F”的時候返回“女”,否則爲“未知”。以此類推。
*/
SELECT DECODE(UPPER(stu.ssex),'M','男','F','女','未知') 性別,
       COUNT(stu.ssex) 人數
  FROM test_z_student stu
 GROUP BY stu.ssex;

---------(31) 查詢姓名以“stu001”開頭的的學生名單 
SELECT  stu.*
  FROM test_z_student stu 
  WHERE stu.sname LIKE 'stu001%';

---------(32) 查詢姓名的前三個字符爲任意字符,且第四、第五個字符以此爲“ss”,其餘的字符爲任意字符的這類同學名單。
/*
考察兩中通配符: % 與 _  
解釋如下:
% 匹配任意多個任意字符
_ 匹配單個任意字符
*/
SELECT stu.*
  FROM test_z_student stu 
  WHERE stu.sname LIKE '___ss%' ;
  
  
---------(33) 查詢每門課程的平均成績,結果按平均成績升序排列,平均成績相同時,按課程號降序排列
SELECT  sc.c# 課程ID,
        TRUNC(AVG(sc.score),2) 課程平均分
  FROM  test_z_sc sc
  GROUP BY sc.c#
  ORDER BY AVG(sc.score) ASC, sc.c# DESC ;

---------(34) 查詢平均成績大於等於60的所有學生的學號、姓名和平均成績 ,並按平均分從高到低排序。
SELECT  sc.s# 學號,
        stu.sname 學生姓名,
        TRUNC(AVG(sc.score),3)  平均分
  FROM test_z_sc sc 
  JOIN test_z_student stu ON(sc.s# = stu.s#)
  GROUP BY sc.s#,stu.sname 
  HAVING AVG(sc.score) >= 60
  ORDER BY TRUNC(AVG(sc.score),3) DESC ;

---------(35) 查詢課程名稱爲“c1-name”,且分數低於60的學生姓名和分數 
SELECT sc.s#  學生號,
       stu.sname 學生姓名,
       sc.score 分數
  FROM  test_z_sc sc
  JOIN test_z_course tc ON(sc.c# = tc.c#)
  JOIN test_z_student stu ON(stu.s# = sc.s#)
  WHERE tc.cname = 'c1-name'
  AND sc.score < 60 ;

---------(36) 查詢所有學生的選課情況; 顯示格式爲: 學生號,學生姓名,課程ID,課程名稱
SELECT stu.s#  學生號,
       stu.sname 學生姓名,
       sc.c# 課程ID,
       tc.cname 課程名稱
  FROM test_z_sc sc
  JOIN test_z_student stu ON(sc.s# = stu.s#)
  JOIN test_z_course tc ON(sc.c# = tc.c#) ;

---------(37) 查詢任何一門課程成績在90分以上的,學生號,學生姓名、課程名稱和分數; 

SELECT sc.s#  學生號,
       stu.sname 學生姓名,
       tc.cname 課程名稱,
       sc.score 分數
  FROM  test_z_sc sc
  JOIN test_z_course tc ON(sc.c# = tc.c#)
  JOIN test_z_student stu ON(stu.s# = sc.s#)
  WHERE sc.score > 90

---------(38) 查詢選修“tchb-1-name”老師所授課程的學生中,成績最高的學生姓名及其成績 
SELECT sc.s# 學生號,
       MAX(sc.score) 分數
  FROM test_z_sc sc 
  JOIN test_z_course tc ON(sc.c# = tc.c#)
  JOIN test_z_teacher tec ON(tc.t# = tec.t# AND tec.tname = 'tchb-1-name')
  GROUP BY sc.s# 
  
---------(39) 查詢各個課程及相應的選修人數  
SELECT sc.c# 課程ID,
       tc.cname 課程名稱,
       COUNT(sc.s#) 選修人數
  FROM test_z_sc sc
  JOIN test_z_course tc ON(sc.c# = tc.c#)
  GROUP BY sc.c# , tc.cname
  ORDER BY 選修人數 DESC, sc.c# ASC ;

---------(40) 查詢不同課程成績相同的學生的學號、課程號、學生成績 
SELECT sc1.s#,sc1.c#,sc1.score FROM test_z_sc sc1 
   JOIN test_z_sc sc2 ON(sc1.score = sc2.score)
   WHERE sc1.c# != sc2.c#
   AND sc1.s# = sc2.s# ;
   --或者
SELECT sc2.s#,sc2.c#,sc2.score FROM test_z_sc sc1 
   JOIN test_z_sc sc2 ON(sc1.score = sc2.score)
   WHERE sc1.c# != sc2.c#
   AND sc1.s# = sc2.s# ;   

---------(41) 查詢每門功成績最好的前兩名 ,請參考(26)
 /*  在此給除你的答案。 */
 
 
---------(42) 按下列要求查詢結果集。參考(39);
/*
統計每門課程的學生選修人數(超過6人的課程才統計)。
要求輸出課程號和選修人數,查詢結果按人數降序排列,查詢結果按人數降序排列,若人數相同,按課程號升序排列 
*/

SELECT sc.c# 課程ID,
       tc.cname 課程名稱,
       COUNT(sc.s#) 選修人數
  FROM test_z_sc sc
  JOIN test_z_course tc ON(sc.c# = tc.c#)
  GROUP BY sc.c# , tc.cname
  HAVING COUNT(sc.s#) > 6
  ORDER BY 選修人數 DESC, sc.c# ASC ;


---------(43) 檢索至少選修4門課程的學生學號。
SELECT sc.s# 學生號,
       COUNT(sc.c#) 選修的課程數
  FROM test_z_sc sc 
  GROUP BY sc.s# 
  HAVING COUNT(sc.c#) >3 ;
  
---------(44) 查詢全部學生都選修的課程的課程號和課程名
SELECT sc.c# 全部學生選的課程ID,
       tc.cname 全部學生選的課程名稱
  FROM test_z_sc sc
  JOIN test_z_course tc ON(tc.c# = sc.c#)
  GROUP BY sc.c#,tc.cname 
  HAVING COUNT(sc.s#)  = (SELECT COUNT(*) FROM test_z_student ) ;

---------(45) 查詢被學生們選修的課程有哪些(也就是說只要被任何學生選修就可以,不一定需要所有學生選修同一門課程)?
select  C#,Cname  
    from  Course  
    where  C#  in  (select  c#  from  sc group  by  c#) ;
    
---------(46) 查詢同時被全部學生選修的那些課程(比如:語文必須被全部學生選修纔算,只要有任何一個沒有選修就不算)
SELECT  tc.c#,
        tc.cname
  FROM test_z_course tc
  WHERE tc.c# IN(SELECT sc.c# FROM test_z_sc sc GROUP BY sc.c# )    

---------(47) 查詢沒學過“tchb-1-name”老師講授的任一門課程的學生姓名 ;
/*
思考:如前面所述,有些問題用正面的方法很難解決,這時不妨用逆向思維。就像以前學數學裏面的
證明題一樣,要求得A、B兩個集合的交集,雖然我求不出來A、B的交集,但能求得A、B的非交集,則
剩下的就是交集了。這裏簡單總結一下,什麼時候用到這種思維方式最多。如下:
當涉及到中間表的過濾,常見於“N”對“N”的關係表,用 “N”的一端作爲條件來過濾關係表,且常見的
條件詞語有“至少”、“全部” 等等。例如本例中,學生表和課程表是多對多的關係,要查詢沒有學過某個
老師的任何一門課課程的學生,這裏注意,如果某個老師教了c1與c2兩門課程,學生1選擇了其中的c1,
即使沒有選擇c2也不能被排除在外,除非c1與c2兩門課程都沒有選。如果直接求交接很難搞,這時我們
換一中思考方式:查詢出只要學過某個老師所教的課程的那些學生的結果集R,然後再查詢學生表只要
不在結果集R中的學生,都是沒有學過該老師的任何一門課程的學生。
*/

SELECT stu.s# 學生號,
       stu.sname 學生姓名
  FROM test_z_student stu
  WHERE stu.s# NOT IN (
    SELECT sc.s# FROM test_z_sc sc
      JOIN test_z_course tc ON(sc.c# = tc.c#)
      JOIN test_z_teacher tec ON(tec.t# = tc.t# AND tec.tname = 'tchb-1-name')
  ) ;

---------(48) 查詢至少有5門課程不及格課程的同學的學號及其平均成績
SELECT sc.s# 學生號,
       AVG(sc.score) 平均分
  FROM test_z_sc sc
  WHERE sc.score < 60
  GROUP BY sc.s#
  HAVING COUNT(sc.s#) >= 5
  
---------(49) 查詢課程編號爲“c1”的課程分數小於70,按分數降序排列的學生學號

SELECT sc.s# 學生號,
       sc.c# 課程號,
       sc.score 分數 
  FROM test_z_sc sc
  WHERE sc.c# = 'c1'
  AND sc.score <70
  ORDER BY sc.score DESC ;

-------------------- 完 -------------------

--  如果你對ORACLE執行計劃感興趣,可以去了解一下如下的1、2、3 。 另外總結一下下面的4、5兩點。
/*
1、 ORACLE plan 的 Filter
2、 ORACLE plan 的 Filter與HASHJOIN比較
3、ORACLE PLAN 什麼時候產生VIEW?
4、JOIN 一個子查詢的時候,子查詢內不能使用外層表的字段作爲條件。如下的AND sc1.c# = sc.c#:
SELECT sc.c#, AVG(sc.score), r_1.gt60
  FROM test_z_sc sc
  JOIN (SELECT sc1.c#, COUNT(sc1.c#) gt60
          FROM test_z_sc sc1
         WHERE sc1.score >= 60
           AND sc1.c# = sc.c# --這裏不能使用sc
         GROUP BY sc1.c#) r_1 ON (r_1.c# = sc.c#)
 GROUP BY sc.c#, r_1.gt60
 ORDER BY AVG(sc.score)
5、總結EXISTS的用法,且區分於NOT IN或者 IN 的區別。
*/

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