1. 程式人生 > >[資料庫] Oracle使用CASE判斷解決多值問題

[資料庫] Oracle使用CASE判斷解決多值問題

        這是最近在使用Oracle資料庫時的一個問題,個人認為是一個非常經典的問題。假設現在有一張專業表,包括如下資訊:


        其中表為:ZY_TAB(ZY_NAME,ZY_CODE,ZY_TYPE,ZY_TIME)。專業表中ZY_NAME表示專業名稱,ZY_CODE表示專業程式碼,ZY_TYPE表示專業型別(包含國家特色專業和省示範專業),ZY_TIME表示設定國家特色專業或省級示範專業的時間。
        例如軟體工程,2004年設為省示範專業,2012年升級為國家特色專業。現在需要輸出如下圖所示的表格:



         如果使用Java後臺處理,通常會將整個ZY_TAB內容讀取,然後依次對專業的ZY_TYPE(優勢專業)進行判斷,判斷主要包括三類:僅為“國家特色專業”,僅為“省示範專業”,同時為“國家特色專業”和“省示範專業”。
        但是總感覺有些彆扭,如果只用SQL語句進行解決,怎麼處理呢?


        方法一:decode函式
        相關知識

        首先想到的方法是使用decode函式判斷。
        decode(型別, '型別1', '值1', '型別2', '值2', '其它')
        它的優勢是可以輸出自定義的值,例如:
        decode(sex, 'Man', '男', 'Woman', '女', '無')
        先存在下表:PERSON(NAME,SEX)表示人的姓名和性別。


select NAME, DECODE(SEX, 'Man', '男', 'Woman', '女', '無') as SEX
from PERSON;

        通常使用 decode(b, 0, 0, a/b) 防止a/b中分母b為0報錯,它的具體含義是如果分母b為0,則SQL返回0,否則返回a/b,相當於執行除法操作。
        再如重新命名為自己喜歡的名稱,如null自定義命名:nvl(SEX,'定義為空'),它等價於 decode(SEX, NULL, '定義為空', SEX)。

        具體操作

        這裡想使用:
        decode(count(ZY_TYPE), '1', '省示範專業', '2', '省示範、國家特色', '無')
select ZY_NAME, 
    DECODE(count(ZY_NAME), '1', '省示範專業', '2', '省示範、國家特色專業', '無') as TYPE
from ZY_TAB GROUP BY ZY_NAME;
        輸出如下圖所示結果,其錯誤是當count(ZY_NAME)=1時,“國家特色專業”也變成了“省示範專業”,所以僅僅通過判斷出現的次數方法不太好。



方法二:使用case語句
        相關知識

        例如需要判斷職員的工資小於或等於2000元時,返回訊息“過低”,大於或等於4000時返回訊息“過高”,其餘返回“正常”。


        這種需求通常會遇到,此時需要使用CASE WHEN來判斷轉換,程式碼如下所示:
select NAME, SEX, SAL, 
    CASE WHEN SAL<=2000 THEN '過低'
         WHEN SAL>=4000 THEN '過高'
         ELSE '正常'
     END AS STATE
from PERSON
order by NAME;
        輸出如下圖所示:


        再舉個例子,下面SQL程式碼是統計各個學位的職工人數:  
SELECT COUNT(*) AS 總人數,  
    COUNT(CASE WHEN HIGHEST_DEGREE='博士'THEN 1 END) AS 博士人數,   
    COUNT(CASE WHEN HIGHEST_DEGREE='碩士'THEN 1 END) AS 碩士人數,   
    COUNT(*)-COUNT(CASE WHEN HIGHEST_DEGREE='博士'THEN 1 END)-COUNT(CASE WHEN HIGHEST_DEGREE='碩士'THEN 1 END) AS 其他學歷  
FROM TEACHER;  
        COUNT(CASE WHEN HIGHEST_DEGREE='博士' THEN 1 END) AS NUM2
        表示當最高學歷HIGHEST_DEGREE欄位為'博士'時,統計數量加1。
        當然如果需要計算學院各個班級的總人口,可以採用使用下面的SQL:
        COUNT(CASE WHEN DW_NAME='軟體學院' THEN NUM_STU END) AS NUM2
        也可以使用提到的CASE防止除法計算分母為0,ZS總數、SHSJ社會實踐人數。即:
round((case when ZS!=0 then SHSJ/ZS else 0 end),3) as bl

        具體操作
        此時需要使用CASE WHEN來判斷,這裡使用了兩次CASE WHEN,第一次是判斷是“國家特色專業”或“省示範專業”,第二次是判斷該專業出現的次數,如果出現兩次則表示兩個型別都存在。
        原始資料如下圖所示:



        SQL程式碼如下所示:
select ZY_NAME, 
CASE WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' OR ZY_TYPE='國家特色專業' THEN 1 END)='2' 
     THEN '省示範專業; 國家特色專業' 
     WHEN COUNT(CASE WHEN ZY_TYPE='國家特色專業' THEN 1 END)='1' 
     THEN '國家特色專業'  
     WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' THEN 1 END)='1' 
     THEN '省示範專業'  
ELSE NULL END
AS TYPE 
from ZY_TAB group by ZY_NAME;
        使用group by防止專業名稱重複,輸出結果如下圖所示:


        如果需要增加專業的其他資訊,如“專業程式碼”,如下程式碼所示:
select ZY_NAME, ZY_CODE, 
CASE WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' OR ZY_TYPE='國家特色專業' THEN 1 END)='2' 
     THEN '省示範專業; 國家特色專業' 
     WHEN COUNT(CASE WHEN ZY_TYPE='國家特色專業' THEN 1 END)='1' 
     THEN '國家特色專業'  
     WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' THEN 1 END)='1' 
     THEN '省示範專業'  
ELSE NULL END
AS TYPE 
from ZY_TAB group by ZY_NAME, ZY_CODE;
        group by 分組中增加ZY_CODE,輸出如下圖所示:



        進階篇
        假設現在存在一張專業表ZY,僅僅儲存專業名稱和專業程式碼,需要通過連線兩張表ZY(專業表)和ZY_TAB(優勢專業表)來統計各專業的優勢專業資訊,怎麼處理呢?
        ZY(NAME,CODE,INFO,PLACE)對應(專業名稱,專業程式碼,資訊,位置)。



        因為通常資料庫設計中,都會設定專業表,再通過外來鍵來關聯其他的專業資訊,包括優勢專業、專業老師情況、專業學生情況、教學情況等等,所以通常需要通過ZY_CODE專業程式碼來進行關聯。
select NAME, CODE, 
	(select
		CASE WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' OR ZY_TYPE='國家特色專業' THEN 1 END)='2' 
     	THEN '省示範專業; 國家特色專業' 
     	WHEN COUNT(CASE WHEN ZY_TYPE='國家特色專業' THEN 1 END)='1' 
     	THEN '國家特色專業'  
     	WHEN COUNT(CASE WHEN ZY_TYPE='省示範專業' THEN 1 END)='1' 
     	THEN '省示範專業'  
	ELSE NULL END 
		from ZY_TAB 
		where ZY_TAB.ZY_CODE=ZY.CODE
	) AS TYPE, INFO, PLACE
from ZY;
        輸出如下圖所示:


        最後希望文章對你有所幫助,主要講述了使用DECODE函式和CASE判斷多值問題,當然如果多個型別也是可以判斷並多指輸出的,但建議通常判斷該兩個型別,要麼輸出A,要麼輸出B,要麼輸出A和B。
        還是那句話,資料庫相關的知識,只有當你真正遇到這個需求的時候對你幫助才會非常大,否則你也可以把它當成簡單基礎知識回顧。
      (By:Eastmount 2016-7-20 晚上8點  http://blog.csdn.net/eastmount/