SQL語句不通過子查詢取某欄位最大的那一條記錄
直接用一個例子來解釋吧,我們要取賬戶表中取最新餘額,也就是取user_acct中每個user_id的pt_d最大的那條記錄的acct_bal
表結構和資料如下
mysql> select * from user_acct;
+---------+----------+----------+| user_id | acct_bal | pt_d |
+---------+----------+----------+
| A | 3000 | 20180101 |
| A | 900 | 20180102 |
| A | 2000 | 20180103 |
| B | 5000 | 20180102 |
| B | 10000 | 20180106 |
| B | 9000 | 20180107 |
+---------+----------+----------+
通常的做法是先寫一個子查詢取出每個user_id的最大pt_d,然後用user_acct表關聯這個子查詢得出結果,這種sql比較簡單,下面是一個例子:
select a.*
from user_acct a
inner join (select user_id
,max(pt_d) as pt_d
from user_acct
group by user_id
) b
on a.user_id = b.user_id
and a.pt_d = b.pt_d
;
但是,如果不用子查詢能不能做出來呢?答案是能,目前看來至少有以下兩種
1.用group by中的having子句
直接上語句吧:
select a.user_id
,a.acct_bal
,a.pt_d
from user_acct a
inner join user_acct b
on a.user_id = b.user_id
group by a.pt_d
having a.pt_d = max(b.pt_d)
;
這種寫法的優點是和子查詢邏輯完全一致,比較通用,缺點是需要join一次,如果資料量大的話,開銷比較大
2.通過拼接、比較、擷取
還是直接上語句吧:
select a.user_id
,split(max(concat(pt_d,'\001',acct_bal)),'\001')[1] as acct_bal
,max(a.pt_d) as pt_d
from user_acct a
group by user_id
;
其中\001是不可見字元,且ascii碼較小。
這種寫法的優點是沒有join,開銷較小,缺點很明顯,就是split函式在mysql和oracle中都不支援,需要自己建立,只有在hive中原生支援,另外還有兩點需要注意:
1.分隔符\001在其他資料庫也不一定支援
2.如果拼接的欄位中有數字型別時,排序可能不對,需要用一些變通的辦法如lpad填充使其排序正確。
總的來說方法1是值得推薦的,方法2的侷限性很大,但是貴在提供了另外一種思考問題的方式。