Oracle函式介紹:decode
1、Oracle函式介紹:decode
Sql程式碼
select sum(DECODE(C810000125,'是',1,0))/COUNT(1) 合格率 FROM t581
統計合格率,如果 C810000125這個欄位為“是”結果1,不為是結果為0
還可以這樣寫:
Sql程式碼
select sum(case when C810000125 = '是' then 1 else 0 end)/COUNT(1) 合格率 FROM t581
含義解釋:
decode(條件,值1,返回值1,值2,返回值2,...值n,返回值n,預設值)
該函式的含義如下:
IF 條件=值1 THEN
RETURN(翻譯值1)
ELSIF 條件=值2 THEN
RETURN(翻譯值2)
......
ELSIF 條件=值n THEN
RETURN(翻譯值n)
ELSE
RETURN(預設值)
END IF
decode(欄位或欄位的運算,值1,值2,值3)
這個函式執行的結果是,當欄位或欄位的運算的值等於值1時,該函式返回值2,否則返回值3
當然值1,值2,值3也可以是表示式,這個函式使得某些sql語句簡單了許多
使用方法:
1、比較大小
select decode(sign(變數1-變數2),-1,變數1,變數2) from dual; --取較小值
sign()函式根據某個值是0、正數還是負數,分別返回0、1、-1
例如:
變數1=10,變數2=20
則sign(變數1-變數2)返回-1,decode解碼結果為“變數1”,達到了取較小值的目的。
2、此函式用在SQL語句中,功能介紹如下:
Decode函式與一系列巢狀的 IF-THEN-ELSE語句相似。base_exp與compare1,compare2等等依次進行比較。
如果base_exp和 第i個compare項匹配,就返回第i 個對應的value 。如果base_exp與任何的compare值都不匹配,則返回default。
每個compare值順次求值,如果發現一個匹配,則剩下的 compare值(如果還有的話)就都不再求值。
一個為NULL的base_exp被認為和NULL compare值等價。如果需要的話,每一個compare值都被轉換成和第一個compare 值相同的資料型別,這個資料型別也是返回值的型別。
Decode函式在實際開發中非常的有用
結合Lpad函式,如何使主鍵的值自動加1並在前面補0
select LPAD(decode(count(記錄編號),0,1,max(to_number(記錄編號)+1)),14,'0') 記錄編號 from tetdmis
eg:
select decode(dir,1,0,1) from a1_interval
dir 的值是1變為0,是0則變為1
比如我要查詢某班男生和女生的數量分別是多少?
通常我們這麼寫:
select count(*) from 表 where 性別 = 男;
select count(*) from 表 where 性別 = 女;
要想顯示到一起還要union一下,太麻煩了
用decode呢,只需要一句話
select sum(decode(性別,男,1,0)),sum(decode(性別,女,1,0)) from 表
補充:同事遇到一個問題,分組的條件是動態變化的,比如:一組資料最多按A、B、C三種條件分組,但是根據情況這三個條件會動態的參與到分組中,有8中情況,例如:按null,按A,按B,按C,按A、B,按A、C,按B、C,按A、B、C。
Sql如下
select t.e
,t.f
,decode(param1,'A',t.a,null) as A
,decode(param2,'B',t.b,null) as B
,decode(param3,'C',t.c,null)as C
from test t
group by
t.e,
t.f,
decode(param1,'A',t.a,null),
decode(param2,'B',t.b,null),
decode(param3,'C',t.c,null)
order by t.e
2、詭異的DECODE函式
今天同事遇到下面一個問題:
order by decode(column_id,1,null,2,null,3,null,column_id);
有個問題,就是當列數大於10列時,column_id 的順序成10,11,12,13,4,5,6,7,8,9了
這個排序的主要目的是讓前3列排在後面,這3列的順序無所謂。
對於小於10列的表是沒問題的:
SQL> create table t(c1 number,c2 number,c3 number,c4number,c5 number);
表已建立。
SQL> col column_name format a20
SQL> select column_name,column_id
2 from user_tab_columns
3 where table_name='T'
4 order bydecode(column_id,1,null,2,null,3,null,column_id);
COLUMN_NAME COLUMN_ID
-------------------- ----------
C4 4
C5 5
C2 2
C1 1
C3 3
但是當表的列數大於10的時候就會混亂了。
SQL> select column_name,column_id,decode(column_id,1,null,2,null,3,null,column_id) sortcolumn
2 from user_tab_columns
3 where table_name='T'
4 order bydecode(column_id,1,null,2,null,3,null,column_id)
5 /
COLUMN_NAME COLUMN_IDSORTCOLUMN
-------------------- ---------- --------------------
C10 10 10
C11 11 11
C12 12 12
C4 4 4
C5 5 5
C6 6 6
C7 7 7
C8 8 8
C9 9 9
C3 3
C2 2
C1 1
已選擇12行。
顯然ORACLE把SORTCOLUMN列作為為字元型別排序了。
加個TO_NUMBER即可解決這個問題。
SQL> select column_name,column_id,decode(column_id,1,null,2,null,3,null,column_id) sortcolumn
2 from user_tab_columns
3 where table_name='T'
4 order byto_number(decode(column_id,1,null,2,null,3,null,column_id));
COLUMN_NAME COLUMN_IDSORTCOLUMN
-------------------- ---------- --------------------
C4 4 4
C5 5 5
C6 6 6
C7 7 7
C8 8 8
C9 9 9
C10 10 10
C11 11 11
C12 12 12
C1 1
C3 3
C2 2
已選擇12行。
但是為什麼會導致這個問題,DECODE函式為何返回了字元型別。
這個問題yangtingkun大師專門寫個幾篇文章介紹。
有興趣的可以找找看看。
在這裡我借花獻佛簡單稍微說一下:
對於NULL 型別,ORACLE的預設返回型別是VARCHAR。
對於DECODE函式 ORACLE返回的型別依賴於第一個值。
如下所示:
SQL> CREATE TABLE A AS SELECTDECODE(DUMMY,'X',1,'Y','2',DUMMY) C1, <---由於第一個返回的值1是整數型別,因此整個表示式返回整數型別
2 DECODE(DUMMY,'X','1','Y',2,DUMMY) C2 ,<---由於第一個返回的值'1'是字元型別,因此整個表示式返回CHAR型別
3 DECODE(DUMMY,'X',NULL,'Y','HUATENG',DUMMY)C3 FROM DUAL; <---由於第一個返回的值是NULL,因此整個表示式返回CHAR型別
表已建立。
SQL> DESC A
名稱 是否為空? 型別
----------------------------------------- ------------------------------------
C1 NUMBER
C2 VARCHAR2(1)
C3 VARCHAR2(7)
也正是因為DECODE函式的這種依賴於第一次的值型別作為返回型別,對於其他返回的值如果和第一個型別不匹配,可能會讓你遇到很蛋疼的問題:
SQL> DESC A;
名稱 是否為空? 型別
----------------------------------------- ------------------------------------
C1 NUMBER
C2 VARCHAR2(1)
C3 VARCHAR2(7)
SQL> INSERT INTO A VALUES(2,2,2);
已建立 1 行。
SQL> SELECT * FROM A;
C1 C2 C3
---------- -- --------------
1 1
2 2 2
SQL> SELECT DECODE(C1,1,1,2,'E',C1) FROM A;
ERROR:
ORA-01722: 無效數字
未選定行
SQL> SELECT DECODE(C1,1,1,2,'E',C1) FROM A WHERE C1=1;
DECODE(C1,1,1,2,'E',C1)
-----------------------
1
SQL> SELECT DECODE(C1,1,1,2,'E',C1) FROM A WHERE C1=2;
SELECT DECODE(C1,1,1,2,'E',C1) FROM A WHERE C1=2
*
第 1 行出現錯誤:
ORA-01722: 無效數字
上面的問題主要是字元'E'無法轉為整數型別導致的。
3、Decode函式返回型別的確定
今天在QQ上一個朋友發出問題,說min函式返回錯誤的取值。詳細如下:一個數據表列型別為number(6,2),其中有三行記錄,分別為0,0.6和1。用min獲取最小值,得到0.6。
min是Oracle SQL的一個基礎函式,理論上不會出現這樣的Bug之類的。下面一起來模擬下實驗環境。
1、環境構建
在實驗資料庫Oracle11g環境下,構建實驗資料表t。填入實驗資料。
SQL> create table t (num number(6,2));
Table created
SQL> insert into t values (0);
1 row inserted
SQL> insert into t values (0.6);
1 row inserted
SQL> insert into t values (1);
1 row inserted
SQL> commit;
Commit complete
SQL> select * from t;
NUM
--------
0.00
0.60
1.00
實驗那位兄弟的說法。
SQL> select min(num) from t;
MIN(NUM)
----------
0
SQL> select min(to_number(num)) from t;
MIN(TO_NUMBER(NUM))
-------------------
0
沒有什麼問題,詳細問了一下,獲取到了SQL結構如下。
SQL> select min(decode(num,-1,null,num)),min(num)from t;
MIN(DECODE(NUM,-1,NULL,NUM)) MIN(NUM)
---------------------------------------- ----------
.6 0
果然,詭異的現象發生了。
2、問題分析
一時間還是很唬人的,那麼我們先拋開min函式,單獨看資料列情況。抽絲剝繭吧。
SQL> select decode(num,-1,null,num),num from t;
DECODE(NUM,-1,NULL,NUM) NUM
---------------------------------------- --------
0 0.00
.6 0.60
1 1.00
這裡只剩下一個decode函式的使用。從含義上看,當num為-1的時候,返回null值,否則就是原有的num值。但是有兩個疑點,首先是0.60是如何轉變為.6的呢?其次就是decode函式處理列的列對其方式,數字型別預設是右對齊,只有字串是左對齊的。難道說經過decode函式處理之後,返回值變成了字串?
那麼,如果decode處理之後,變成了字串的話,我們調整一個decode的結構,看看是否是由於處理變成字串造成了問題。
SQL> selectmin(to_number(decode(num,-1,null,num))),min(num) from t;
MIN(TO_NUMBER(DECODE(NUM,-1,NU MIN(NUM)
------------------------------ ----------
0 0
看來原因就在於decode函式使用處理之後,返回資料列是一個字串型別。但是decode函式命名指定了num列,返回值是什麼型別呢?
num是數字肯定沒有什麼問題?難道說疑點出現在null的返回值型別上?繼續實驗。
SQL> select decode(num,-1,num,num),num from t;
DECODE(NUM,-1,NUM,NUM) NUM
---------------------- --------
0 0.00
0.6 0.60
1 1.00
果然,臨時取消掉null,decode返回型別就正常。看來真是受到了null的影響。這個時候,筆者思考一個問題,Oracle Decode函式如何確定返回值型別列呢?
SQL> select decode(num,-1,'d',num),numfrom t;
DECODE(NUM,-1,'D',NUM) NUM
---------------------------------------- --------
0 0.00
.6 0.60
1 1.00
SQL> select decode(num,-1,'k',num),to_char(num)from t;
DECODE(NUM,-1,'K',NUM) TO_CHAR(NUM)
---------------------------------------- ----------------------------------------
0 0
.6 .6
1 1
上面的實驗,讓我們得出了和null值是相同的效果。這樣,我們對decode有下面猜想:
ü Oracle在呼叫decode函式的時候,是需要預先確定列的型別,因為畢竟出現在相同的列上;
ü 確定decode返回值型別,是依據引數中第一個條件返回型別。之後所有的返回型別都依據第一個型別進行強制型別轉換;
ü Oracle在第一個條件返回型別為null的時候,預設將其作為字串處理;
如果三個假設成立,那麼所有問題就得到解釋。
那個朋友的SQL中,decode函式第一個可選返回值是null,Oracle識別返回型別為字元型別。之後對所有的其他返回值均使用了to_char方法類似的轉換邏輯。
那麼,往後想一步,如果Oracle decode函式真是依靠第一條件來確定列型別,其他列進行強行轉換,那麼如果出現不匹配的時候怎麼辦?
SQL> select decode(num,1,num,'k') from t;
select decode(num,1,num,'k') from t
ORA-01722:無效數字
這個案例中的decode函式,根據第一個前條件取值num是數字型別,那麼其他所有都會被強制轉換為數字型別。但是我們寫定的其他條件取值是’k’,不能進行強制型別轉換。於是報錯無效數字。
3、問題解決
瞭解了問題decode的根源,剩下的就好解釋了。min函式可以接受字串和數字。在數字型別時,依據數字型別的比較規則,選擇出0是最小值。當接受字串時,使用的是二進位制對比策略。其中.小數點的排序位最小。於是選擇出.6作為結果也就不奇怪了。
解決問題的方法很多,筆者推薦的方式是對null進行數字化處理。讓Oracle識別為數字型別。
SQL> select min(decode(num,-1,to_number(null),num)),min(num)from t;
MIN(DECODE(NUM,-1,TO_NUMBER(NU MIN(NUM)
------------------------------ ----------
0 0
4、結論
通過這個案例,我們除了重新認識到decode的原理外,還有幾個收穫。
首先是要重視null值,null在Oracle中是一種很特殊的型別。在運算和函式呼叫中,都有很多特殊之處。遇到問題,要注意考慮null的因素,是我們解決問題的思路;
其次就是重視函式的本質。Decode是我們常見的函式,但是我們對一些細節缺乏思考研究。比如Decode返回值型別如何確定?這些都是細節,但是細節也反映了我們的能力和修行。