mysql左外联另一个表时关联字段是一对多,但只想要最新一条与之关联的优雅的解决方案
mysql左外联另一个表时关联字段是一对多,但只想要最新一条与之关联的优雅的解决方案
需求
需求是这样:我们有表a为人员入场表,表b为人员登记表,但是我们有不能保证人员多次登记,所以这个时候我们只取最新的一条登记数据与入场表以人员id关联
历史解决方案(low的)
按照以往的情况,我可能会把b表进行分组然后排序然后取第一条生成个表与第一个表关联,但是写法特别复杂类似这样:
SELECT
   a.*,v.*
FROM
  a
  LEFT JOIN (
SELECT b.*
FROM b b1
JOIN (
    SELECT user_id, MAX(create_time) AS max_time
    FROM b
    GROUP BY user_id
) b2 ON b1.user_id = b2.user_id AND b1.create_time = b2.max_time;
) v on a.user_id = v.user_id
而且极限情况如果create_time完全相同还会出现问题,
优雅解决方案(mysql 8.0以上)
但是我最近发现个更优雅的写法
SELECT a.*,v.* FROM a LEFT JOIN (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time DESC) AS rn FROM b ) v ON a.user_id = v.user_id AND v.rn = 1
解释:
  PARTITION BY user_id:将结果集按 user_id字段进行分区。每个不同的 user_id 值会形成一个独立的分区。
  ORDER BY create_time DESC:在每个分区内部,按 create_time 字段降序排列。这意味着每个分区内的记录会根据 create_time 从最新到最旧进行排序。
  ROW_NUMBER():为每个分区内的记录分配一个行号,行号从1开始,按 create_time 的降序递增。
mysql5.7替代方案
  那有的同学说了我用的mysql5.7不支持窗口函数怎么办呢?没关系我们可以使用子查询和变量来模拟 ROW_NUMBER() 的行为
SELECT a.*, v.* FROM a LEFT JOIN ( SELECT b.*, @rn := IF(@current_user = user_id, @rn + 1, 1) AS rn, @current_user := user_id FROM b, (SELECT @rn := 0, @current_user := '') AS vars ORDER BY user_id, create_time DESC ) v ON a.user_id = v.user_id AND v.rn = 1;
- 
变量初始化:在子查询中,我们使用一个从 b表和初始化变量的虚拟表(SELECT @rn := 0, @current_user := '' AS vars)的交叉连接。这允许我们在查询中初始化变量@rn和@current_user。
- 
变量赋值:在 SELECT子句中,我们使用@rn变量来存储每个user_id分组的行号。IF(@current_user = user_id, @rn + 1, 1)这部分逻辑用于检查当前行是否与前一行有相同的user_id。如果是,行号递增;如果不是,行号重置为 1。同时,@current_user := user_id用于更新当前user_id的值。
- 
排序:在 ORDER BY子句中,我们按照user_id和create_time DESC对结果进行排序,以确保最新的记录具有行号 1。
- 
外部查询:在外部查询中,我们执行一个左连接,将 a表与我们的子查询结果v连接在一起,只选择每个user_id的最新记录(即v.rn = 1)。
- 
请注意,这种方法依赖于 MySQL 的变量和排序顺序,因此它的性能可能不如直接在支持窗口函数的 MySQL 版本中好。此外,如果表 b非常大,这种方法可能会很慢,因为它需要对整个表进行排序。
本文来自博客园,作者:void_main(),转载请注明原文链接:https://www.cnblogs.com/void--main/p/18689032

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号