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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章