前提條件與需求
主表: 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