SQL條件邏輯——SQL讀書筆記


《SQL學習指南》中的第11章

  
 1.1 概念:
     條件邏輯:條件邏輯是程序執行時從多個路徑中選取其一的能力
     1)簡單例子演示:
         例子1:查詢客戶信息時根據客戶類型從individual表中檢索fname/lname列或者從business表中
 獲取name列 (左外連接)
        
         SELECT c.cust_id,c.fed_id,c.cust_type_cd,
    CONCAT(i.fname,' ',i.lname) AS indv_name,
    b.`name` AS business_name
    FROM customer c
    LEFT JOIN individual i
    ON c.cust_id = i.cust_id
    LEFT JOIN business b
    ON c.cust_id = b.cust_id;
       結果如圖所示
       

                    

           例子2:查詢客戶信息時根據客戶類型從individual表中檢索fname/lname列或者從business表中
 獲取name列 (使用case表達式使用條件邏輯決定客戶類型,進而返回恰當的字符串)
            
             SELECT c.cust_id, c.fed_id,
    CASE
        WHEN c.cust_type_cd = 'I'
            THEN CONCAT(i.fname,' ',i.fname)
        WHEN c.cust_type_cd = 'B'
            THEN b.`name`
        ELSE 'Unkown'
    END AS `name` -- 給這個條件判斷的結果取個別名
    FROM customer c
    LEFT JOIN individual i
    ON c.cust_id = i.cust_id
    LEFT JOIN business b
    ON c.cust_id = b.cust_id;
           
          結果如圖所示  
                

          分析:查詢只返回由case表達式生成的單個name列,這個從查詢的第二行起的case表達式首先檢查cust_type_cd列的值,然后依據該值決定返回個人名稱還是企業名稱。

       
 1.2 case表達式
     主流的數據庫服務器中模擬條件判斷的內置函數包括:Oracle的decode()函數,MySQL中if()函數以及SQL Server的coalesce()函數。
       case表達式作為一種條件邏輯表達式,具備以下特點:
          case表達式是SQL標准的一部分(SQL92),並且在多種數據庫中實現;
          case表達式已經內置於SQL語法,可以用於select,insert,update和delete語句。
      下面介紹兩種不同類型的case表達式
       1)查找型case表達式,其語法如下:
          CASE
               WHEN C1 THEN E1
               WHEN C2 THEN E2
               ....
               [ELSE ED]
          END AS 別名
       其中C1,C2....代表條件,E1,E2....表示case表達式返回的表達式結果,else子句是可選的。
       注意:case表達式返回的類型可以為日期型,數字性,字符串類型等,但是同一個case表達式中每個THEN返回的表達式結果必須相同
          例子1:使用子查詢代替外連接從individual和business表中檢索個人名稱/企業名稱
                 SELECT c.cust_id,c.fed_id,
    CASE
        WHEN c.cust_type_cd = 'I'
        THEN (
            SELECT CONCAT(i.fname,' ',i.lname)
            FROM individual i
            WHERE i.cust_id = c.cust_id
        )
        WHEN c.cust_type_cd = 'B'
        THEN (
            SELECT b.`name`
            FROM business b
            WHERE b.cust_id = c.cust_id
        )
     END  AS `name`
                  FROM customer c
               
          結果如圖所示  
                       

        2)簡單case表達式,其語法如下:

     CASE V0
        WHEN V1 THEN E1
        WHEN V2 THEN E2
        .....
        [ELSE END]
     END
    簡單case表達式主要是通過自動構建等式條件,通過對V1,V2....與V0的值進行匹配,然后返回相應的表達式結果  
         
               例子1.下面修改1.1中例子1那個查找型case表達式
             SELECT c.cust_id, c.fed_id,
    CASE  c.cust_type_cd
        WHEN 'I'
            THEN CONCAT(i.fname,' ',i.fname)
        WHEN 'B'
            THEN b.`name`
        ELSE 'Unkown'
    END AS `name` -- 給這個條件判斷的結果取個別名
    FROM customer c
    LEFT JOIN individual i
    ON c.cust_id = i.cust_id
    LEFT JOIN business b
    ON c.cust_id = b.cust_id;
          
          結果如圖所示  
                       

 

          分析:上面的例子將查找型case表達式轉化成簡單case表達式,注意這里由於查找型case表達式的條件單一,這樣轉換並不會出現什么問題,但是在范圍條件,不等條件以及基於and/or/not這些運算符的復合條件對於簡單case條件並不適用。
 
          
 1.3 case表達式用途
      case表達式適用於那些場景:1.結果集變換,2.選擇性聚合,3.存在性檢查,4.除0失誤,5.有條件更新,6.null值處理
      1)結果集變換
         
          結果集變換:對結果集的顯示形式進行變換,如多行轉列
    
     例子1.查詢展示從2000年到2005年每年的開戶數目:
         SELECT YEAR(a.open_date) `YEAR`,
COUNT(*) YearCount
FROM account a
WHERE (a.open_date >= '2000-01-01'
AND a.open_date <= '2005-12-30'
)
GROUP BY YEAR(a.open_date);
 
       結果如圖所示
          

     例子2.將上面的結果變換成單行多列顯示

    SELECT
    SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2000
                THEN 1
                ELSE 0
        END    ) year_2000,
    SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2001
                THEN 1
                ELSE 0
        END    )  year_2001,
    SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2002
                THEN 1
                ELSE 0
        END    )  year_2002,
    SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2003
                THEN 1
                ELSE 0
        END    ) year_2003,
    SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2004
                THEN 1
                ELSE 0
        END    ) year_2004,
        SUM(
        CASE
                WHEN EXTRACT(YEAR FROM a.open_date) = 2005
                THEN 1
                ELSE 0
        END    )  year_2005
     FROM account a;
     
       結果如圖所示
       

       分析:這種少量數據的由行轉列的可以這樣實現,但是當行數過多時,那就要用到后面的解決辦法了。

       
  2)選擇性聚合
      
          選擇性聚合:通過判斷條件進行對某些數據進行查找,篩選,聚合
         
      例子1.查找account表中那些賬戶余額與transaction表中賬戶余額,代收余額不相符的地方。
         分析:
          1)由於交易賬戶總是正的,所以讀者需要查看交易類型是借款('DBT')還是存款('CBT'),借款則應該將金額數變成負的(乘以-1);
          2) 如果funds_avail_date列中的日期大於當前日期(未到期),交易應該被加到代收余額總和,而不是可用余額總和;
          3)同時,有些交易需要被排除在可用余額之外,而所有交易應該都被包含在代收余額之內。
    
     SELECT a.account_id AS unbalance_account_id
FROM account a
WHERE (a.avail_balance, a.pending_balance) <>( -- avail_balance賬戶余額,pending_balance 代收余額
        SELECT
            SUM(
            CASE
                WHEN t.funds_avail_date > CURRENT_TIMESTAMP()
                THEN 0
                WHEN t.txn_type_cd = 'DBT'
                THEN t.amount * -1
                ELSE t.amount
            END
        ),
        SUM(
            CASE
                WHEN t.txn_type_cd = 'DBT'
                THEN  t.amount * -1
                ELSE t.amount
            END
        )
        FROM `transaction` t
        WHERE t.account_id = a.account_id
     )
      結果如圖所示
     

     3)存在性檢查

         
          存在性檢測:對某些數據進行是否存在進行判斷,或者對數據量進行統計  
         
     例子1.查詢客戶是否存在支票賬戶或者儲蓄賬戶
         SELECT c.cust_id,c.fed_id,c.cust_type_cd,
CASE
    WHEN EXISTS(
        SELECT 1 FROM account a
        WHERE a.cust_id = c.cust_id
        AND a.product_cd = 'CHK'
    )
  THEN 'Y'
    ELSE 'N'
END AS has_checking
,
CASE
    WHEN EXISTS(
        SELECT 1 FROM account a
        WHERE a.cust_id = c.cust_id
        AND a.product_cd = 'SAV'
    )
    THEN 'Y'
    ELSE 'N'
END AS has_saving
FROM customer c;
           結果如圖所示
          

          分析: 每個case表達式包含了一個對account表的關聯子查詢:一個查找支票賬戶,另一個查找儲蓄賬戶。

          由於每一個when子句都使用了exists運算符,因此只要客戶至少存在一個相應的賬戶那么條件為真
         
          例子2. 使用簡單case表達式為每個客戶計算賬戶數目,然后返回None,1,2,3+
             SELECT
CASE COUNT(a.account_id)
    WHEN 0 THEN 'None'
    WHEN 1 THEN '1'
    WHEN 2 THEN '2'
    ELSE '3+'
END AS AccountCount
,c.cust_id
FROM customer c
RIGHT JOIN account a
ON c.cust_id = a.cust_id
GROUP BY c.cust_id;
         
          結果如圖所示
          

        4)除0錯誤

          
      除0錯誤檢測 :執行除法運算時避免分母為0的情況,進行判斷。同時不同數據庫對除0出錯進行不同處理方法,Oracle在遇到0分母時會拋出一個錯誤,而MySQL只是簡單的將結果值置為null.
 
     例子1.查詢計算同一產品類型的所有賬戶的每個賬戶余額與總余額的比率
          
         SELECT a.product_cd,SUM(a.avail_balance)
FROM  account a
GROUP BY a.product_cd;
 
SELECT an.cust_id,an.product_cd,an.avail_balance/
CASE
    WHEN newCount.totalAvail = 0 THEN 1
  ELSE newCount.totalAvail
END AS rate
FROM account an INNER JOIN (
    SELECT a.product_cd,SUM(a.avail_balance) AS totalAvail
    FROM  account a
    GROUP BY a.product_cd
) AS newCount
ON an.product_cd = newCount.product_cd;
 
結果如圖所示
     
      5)有條件更新
          
      有條件更新:更新表中的行時,常常需要指定的列應該置什么值,但這個值往往需要根據其他的表中數值進行判斷,才對該值進行更新。
   
     例子1.假定插入一個ID為999的一個交易,但此時需要修改account表中avail_balance,prending_balance和last_activity_date這3列的值,后兩個值比較容易更新,更新avavil_balance列則必須檢查transaction表的funds_avail_date列判斷交易資金是否立即可以使用。
         UPDATE account a
SET a.last_activity_date = CURRENT_TIMESTAMP(),
a.pending_balance = a.pending_balance + (
    SELECT t.amount*
        CASE
            WHEN t.txn_type_cd = 'DBT'
            THEN -1
            ELSE  1
        END
    FROM `transaction` t
    WHERE t.txn_id = 22
),
a.avail_balance = a.avail_balance + (
    SELECT t.amount*
     CASE
            WHEN t.funds_avail_date > CURRENT_TIMESTAMP() THEN 0
            WHEN t.txn_type_cd = 'DBT' THEN -1
            ELSE 1
        END
    FROM `transaction` t
    WHERE t.txn_id = 22
)
WHERE a.account_id = (
    SELECT t.account_id
    FROM `transaction` t
    WHERE t.txn_id = 22
)
         修改之前的數據:
         

                    修改之后的數據:

         

                    在transaction中插入的那條數據

          

                   分析:這個語句共包含2個case語句,第一個case表達式對交易賬戶金額進行判斷,是否是存款還是借款

          第二個case表達式進行兩種判斷,首先用於檢查資金的可用性日期,如果日期是未來,則只對可用余額加0
          否則,返回1;然后 對交易賬戶金額進行判斷,是否是存款還是借款,存款返回1,借款返回-1。
          
         
       6)null值處理
          
       null值處理:null是某列的值未知時存儲到表中的值,不過檢索時顯示null值或者null參與表達式運算會出現錯誤
  
     樣例: SELECT <some calculation> +
              CASE 
                WHEN avail_balance IS NULL THEN 0
                ELSE avail_balance
              END
            +  <some calculation>
 
 
 


免責聲明!

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



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