mysql左外联另一个表时关联字段是一对多,但只想要最新一条与之关联的优雅的解决方案

mysql左外联另一个表时关联字段是一对多,但只想要最新一条与之关联的优雅的解决方案

需求

  需求是这样:我们有表a为人员入场表,表b为人员登记表,但是我们有不能保证人员多次登记,所以这个时候我们只取最新的一条登记数据与入场表以人员id关联

历史解决方案(low的)

  按照以往的情况,我可能会把b表进行分组然后排序然后取第一条生成个表与第一个表关联,但是写法特别复杂类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
复制代码
  1. 变量初始化:在子查询中,我们使用一个从 b 表和初始化变量的虚拟表 (SELECT @rn := 0, @current_user := '' AS vars) 的交叉连接。这允许我们在查询中初始化变量 @rn 和 @current_user

  2. 变量赋值:在 SELECT 子句中,我们使用 @rn 变量来存储每个 user_id 分组的行号。IF(@current_user = user_id, @rn + 1, 1) 这部分逻辑用于检查当前行是否与前一行有相同的 user_id。如果是,行号递增;如果不是,行号重置为 1。同时,@current_user := user_id 用于更新当前 user_id 的值。

  3. 排序:在 ORDER BY 子句中,我们按照 user_id 和 create_time DESC 对结果进行排序,以确保最新的记录具有行号 1。

  4. 外部查询:在外部查询中,我们执行一个左连接,将 a 表与我们的子查询结果 v 连接在一起,只选择每个 user_id 的最新记录(即 v.rn = 1)。

  5. 请注意,这种方法依赖于 MySQL 的变量和排序顺序,因此它的性能可能不如直接在支持窗口函数的 MySQL 版本中好。此外,如果表 b 非常大,这种方法可能会很慢,因为它需要对整个表进行排序。

posted @   void_main()  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示