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的局限性很大,但是贵在提供了另外一种思考问题的方式。

猜你喜欢

转载自blog.csdn.net/xiaozengtongxue/article/details/80811774