1. 程式人生 > >SQL進階(上)

SQL進階(上)

CASE表示式

新手用WHERE字句進行條件分支,高手用SELECT字句進行條件分支

--男性人口
SELECT pref_name,
        SUM(population)
FROM PopTbl2
WHERE sex='1'
GROUP BY pref_name
--女性人口
SELECT pref_name
        SUM(population)
FROM PopTbl2
WHERE sex='2'
GROUP BY pref_name
SELECT pref_name,
        --男性人口
        SUM(CASE WHEN sex='1' THEN population ELSE 0 END) AS cnt_m,
        --女性人口
        SUM(CASE WHEN sex='2' THEN population ELSE 0 END)AS cnt_f,
FROM PopTbl2
GROUP BY pref_name;

新手用HAVING子句進行條件分支,高手用SELECT子句進行條件分支

--選擇只加入一個社團的學生

SELECT std_id,MAX(club_id)AS main_club
FROM    StudentClub
GROUP BY std_id
HAVING COUNT(*)=1;

--選擇加入了多個社團的學生
SELECT std_id,club_id AS main_club
FROM    StudentClub
WHERE    main_club_flg='Y';
SELECT std_id,
        CASE WHERE COUNT(*)=1        --只加入一個社團的學生
            THEN MAX(club_id)
            ELSE MAX(CASE WHEN main_club_flag='Y'
                          THEN club_id
                          ELSE NULL END)
        END AS main_club
FROM    StudentClub
GROUP BY std_id

自連線的用法

刪除重複行

DELETE FROM Products P1
WHERE EXISTS(SELECT *
                FROM Products P2
                WHERE P1.name =P2.name
                AND P1.price=P2.price
                AND P1.rowid <P2.rowid);

查詢區域性不一致的列

--用於查詢價格相等但商品名稱不同的記錄的SQL語句
SELECT DISTINCT P1。name,P1.price
FROM Products P1,Products P2
WHERE P1.price =p2.price
AND P1.name<>P2.name

排序

--排序,使用視窗函式
SELECT name,price
    RANK() OVER (ORDER BY price DESC) AS rank_1,
    DENSE_RANK() OVER(ORDER BY price DESC)AS rank_2
FROM Products

三值邏輯和NULL

true和false兩個值,第三個值unknown

  1. 排中律不成立
  2. NOT IN和NOT EXISTS不是等價的

HAVING子句的力量

尋找缺失的編號

--如果有查詢結果,說明存在缺失的編號
SELECT '存在缺失的編號' AS gap
FROM SeqTbl
HAVING COUNT(*)<>MAX(seq)

求眾數

SELECT income,COUNT(*)AS cnt
FROM Graduates
GROUP BY income
HAVING COUNT(*) >=ALL(SELECT COUNT(*)
                        FROM Graduates
                        GROUP BY income)

求中位數

--求中位數SQL語句:在HAVING子句中使用非等值自連線
SELECT AVG(DISTINCT income)
FROM (SELECT T1.income
        FROM Graduates T1,Graduates T2
        GROUP BY T1.income)
        --S1的條件
HAVING SUM(CASE WHEN T2.income >=T1.income THEN 1 ELSE 0 END)>=COUNT(*)/2
AND SUM(CASE WHERE T2.income<=T1.income THEN 1 ELSE 0 END)>=COUNT(*)/2) TEP

查詢不包含NULL的集合

--在對包含NULL的列使用時,COUNT(*)和COUNT(列名)的查詢結果是不同的
SELECT COUNT(*),COUNT(col_1)
FROM NUllTbl

外連線的用法

用外連線進行行列轉換(行→列)

--水平展開求交叉表
SELECT C0,name,
    CASE WHEN C1.name IS NOT NULL THEN 'O' ELSE NULL END AS "SQL入門"
    CASE WHEN C2.name IS NOT NULL THEN 'O' ELSE NULL END AS "UNIX基礎"
    CASE WHEN C3.name IS NOT NULL THEN 'O' ELSE NULL END AS "Java中級"
FROM (SELECT DISTINCT name FROM Courses)CO --這裡的Co是側欄
LEFT OUTER JOIN
    (SELECT name FROM Courses WHERE course ='SQL入門')C1 ON CO.name =C1.name
LEFT OUTER JOIN
    (SELECT name FROM Courses WHERE course='UNIX基礎')C2 ON C0.name=C2.name
LEFT OUTEER JOIN
    (SELECT name FROM Courses WHERE course='Java中級')C3 ON C0.name=C3.name

用外連線進行行列轉換(列→行)

--列資料轉換成行資料;使用UNION ALL
SELECT employee,child_1 AS child FROM Personnel UNION ALL
SELECT employee,child_2 AS child FROM Personnel UNION ALL
SELECT employee,child_3 AS child FROM Personnel

用外連線求差集:A-B

SELECT A.id AS id,A.name AS A_name
FROM Class_A A LEFT OUTER JOIN CLass_B B
ON A.id =B.id
WHERE B.name IS NULL

用全外連線求異或集

SELECT COALESCE(A.id,B.id)AS id,
    COALESCE(A.name,B.name)AS name
FROM Class_A A FULL OUTER OUTER JOIN Class_B B
ON A.id=B.id
WHERE A.name IS NULL
OR B.name IS NULL

用關聯子查詢比較行與行

和上一年比較結果

--求出的是增長了還是減少了,亦或者是維持現狀,使用關聯子查詢
SELECT S1.year,S1.sale,
        CASE WHEN sale=
            (SELECT sale
            FROM Sales S2
            WHERE S2.year =S1.year-1)THEN '→'--持平
        WHEN sale>
            (SELECT sale
            FROM Sales S2
            WHERE S2.year =S1.year-1)THEN '↑'--增長
        WHEN sale<
            (SELECT sale
            FROM Sales S2
            WHERE S2.year =S1.year-1)THEN '↓'--減少
        ELSE '-' END AS var
FROM Sales S1
ORDER BY year;

和過去最臨近的年份營業額相同的年份,同時使用自連線

SELECT S1.year AS year,
    S1.year AS year,
FROM Sale2 S1,Sales2 S2
WHERE S1.sale =S2.sale
AND S2.year =(SELECT MAX(year)
              FROM Sales2 S3
                WHERE S1.year>S3.year)
ORDER BY year;

移動累計值

--求累計值;使用肥羅已滿型遞迴集合
SELECT prc_data, A1.prc_amt,
        (SELECT SUM(prc_amt)
        FROM Accounts A2
        WHERE A1.prc_date >=A2.prc_date)AS onhand_amt
FROM Accounts A1
ORDER BY prc_date;

查詢重疊的時間區間

--求重疊的住宿期間
SELECT reserver,start_date,end_date
FROM Reservations R1
WHERE EXISTS
    (SELECT *
    FROM Reservations R2
    WHERE R1.reserver<> R2.reserver    --與自己以外的客人進行比較
    AND (R1.start_date BETWEEN R2.start_date AND R2.end_date    
                    --自己的入住日期在他人的住宿期間
    OR R1.end_date BETWEEN R2.start_date AND R2.end_date));
                    --自己的離店日期在他人的住宿期間內