oracle 中的ROW_NUMBER() OVER() 的用法以及如何把空值轉化指定值——記一次查詢

前提條件與需求

主表: ag_ba_old_base
從表: AG_BA_OLD_MEDICAL
字典表: jz_dictionary

在從表中有ag_ba_old_base_id 這個字段是和主表關聯的,但是由於各種各樣的原因導致從表中有很多重複數據,我們需要從中篩選出同樣ag_ba_old_base_id 中 最新的更新日期 ,如果沒有更新日期那麼用id來比較,id比較大的優先留着。

如下圖

select t.ag_ba_old_base_id ,count(id) from AG_BA_OLD_MEDICAL t where t.create_date is not null  
group by t.ag_ba_old_base_id 
having count(id) >3

實際上重複兩次和三次的也很多 這裏只是爲了演示一下重複數據

這裏寫圖片描述

我們找一個重複次數比較多的數據來看

select t.id,t.ag_ba_old_base_id,t.create_date
from AG_BA_OLD_MEDICAL t 
where ag_ba_old_base_id = 81904287
order by t.ag_ba_old_base_id,t.create_date desc,t.id desc

這裏寫圖片描述

這裏我們可以看到重複數據之間的區別是 create_date 和此表類似於主鍵的id(之所以這麼說是因爲此表非常不規範 不能按照主鍵處理),而且接到任務時被告知這表中可能存在id小但是create 但是create_date 更新的情況。所以在篩選時不能單單利用max()函數來直接分組查詢,這給這次查詢增加了一點難度。但是經過仔細思考發現用order by 完全可以解決

order by t.ag_ba_old_base_id,t.create_date desc,t.id desc

經過查詢發現oracle 和mysql很不一樣,其中有一點就是mysql中的top 在oracle中是需要藉助子查詢才能實現,具體情況如下

oracle 中的 top 怎麼實現

參考http://www.cnblogs.com/MyFavorite/archive/2012/10/31/2748936.html

在SQL Server裏面有top關鍵字可以很方便的取出前N條記錄,但是Oracle裏面卻沒有top的使用,類似實現取出前N條記錄的簡單方法如下:

方法1:利用ROW_NUMBER函數

SELECT NO FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY NO) RNO, NO FROM ROWNUM_TEST
)WHERE RNO <= 5  ORDER BY NO ;

方法2:利用子查詢

取出前5條記錄:

SELECT NO FROM (
    SELECT NO FROM ROWNUM_TEST  ORDER BY NO
)WHERE ROWNUM <= 5  ORDER BY NO ;

第二種情況經過測試發現並沒有第一種好用,因爲rownum是數據在原始表中的順序,並不是此次select的數據的順序。

那麼第一種我們來看看它是怎麼用的

ROW_NUMBER() OVER() 分析函數的用法

參考http://blog.csdn.net/iw1210/article/details/11937085

ROW_NUMBER() OVER(partition by col1 order by col2) 表示根據col1分組,在分組內部根據col2排序,而此函數計算的值就表示每組內部排序後的順序編號(組內是連續且唯一的)。

舉例:

SQL> DESC T1;

Name Null? Type
ID NUMBER
NAME VARCHAR2(10)
DATE1 DATE

SQL> SELECT * FROM T1;

    ID NAME                                DATE1
   101 aaa                                  09-SEP-13
   101 bbb                                  10-SEP-13

   101 ccc                                  11-SEP-13

   102 ddd                                  08-SEP-13

   102 eee                                  11-SEP-13
SQL> SELECT ID,NAME,DATE1,ROW_NUMBER() OVER(partition by ID order by DATE1 desc) as RN FROM T1;
   ID NAME                  DATE1                 RN
   101 ccc                  11-SEP-13              1
   101 bbb                  10-SEP-13              2
   101 aaa                  09-SEP-13              3
   102 eee                  11-SEP-13              1
   102 ddd                  08-SEP-13              2

把上面語句作爲一個子表語句,嵌入到另一條語句中:

SQL> SELECT ID,NAME,DATE1 FROM (SELECT ID,NAME,DATE1,ROW_NUMBER() OVER(partition by ID order by DATE1 desc) as RN FROM T1)T WHERE T.RN=1;
    ID NAME                                 DATE1
   101 ccc                                  11-SEP-13
   102 eee                                  11-SEP-13

那麼最終經過思考寫出的sql語句如下

select base.id,base.name,c.id,c.ag_ba_old_base_id,jz.name
 from ag_ba_old_base base
left join
        (
                select id,ag_ba_old_base_id,create_date,medical_ensure_type from (
                select t.id,t.ag_ba_old_base_id,t.medical_ensure_type,t.create_date,ROW_NUMBER() OVER(partition by t.ag_ba_old_base_id order by t.ag_ba_old_base_id,t.create_date desc,t.id desc) as asd
                            from AG_BA_OLD_MEDICAL t 
                            where ag_ba_old_base_id = t.ag_ba_old_base_id 
                )b          
                where asd =1 
        )c
on c.ag_ba_old_base_id = base.id
left join jz_dictionary jz on jz.id =  c.medical_ensure_type

結果如下圖

這裏寫圖片描述

發現最終的結果中有空的情況存在,也就是null ,所以要藉助下面這個函數

如何把控制轉化成指定值

NVL(Expr1,Expr2)如果Expr1爲NULL,返回Expr2的值,否則返回Expr1的值

例如:select NVL(SUM(MONEY) ,0) from tb全都在NVL這兒起作用

其它:

NVL2(Expr1,Expr2,Expr3)如果Expr1爲NULL,返回Expr2的值,否則返回Expr3的值

NULLIF(Expr1,Expr2)如果Expr1和Expr2的值相等,返回NULL,否則返回Expr1的值

最後的終結版sql

所以最終版的sql就如下:

select base.id,base.name,c.id,c.ag_ba_old_base_id,NVL(jz.name,'未知')as name
 from ag_ba_old_base base
left join
        (
                select id,ag_ba_old_base_id,create_date,medical_ensure_type from (
                select t.id,t.ag_ba_old_base_id,t.medical_ensure_type,t.create_date,ROW_NUMBER() OVER(partition by t.ag_ba_old_base_id order by t.ag_ba_old_base_id,t.create_date desc,t.id desc) as asd
                            from AG_BA_OLD_MEDICAL t 
                            where ag_ba_old_base_id = t.ag_ba_old_base_id 
                )b          
                where asd =1 
        )c
on c.ag_ba_old_base_id = base.id
left join jz_dictionary jz on jz.id =  c.medical_ensure_type

這裏寫圖片描述

發佈了83 篇原創文章 · 獲贊 103 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章