首页 > 解决方案 > 如何在 MySQL 5.7 中进行相当于横向连接的操作?

问题描述

我正在尝试从两个表中获取信息,一个表包含有关对象 A 的一般数据,另一个表包含该对象的补充数据的历史记录。

我想要做的是从 A 中获取所有记录以及 B 的相应最新实例(最高 id),请求速度很快,两个表都很大。B 有一个密钥,允许它识别它链接到哪个 A,因为它是一对多关系。我还想从第三个表 C 中获取信息,我可以从 B 中存在的两个字段中获取这些信息。给定的 B 也可能没有 C 数据,我也想知道。

从我在论坛上搜索的内容来看,横向连接似乎是解决方案,但在我的 MySQL 版本中不存在。

我还希望能够在 A 和 B 列值中过滤具有可变条件的查询。

我现在正在尝试的(如果我可以等待几个小时,则可以使用)是:

SELECT a.*, b.*, c.* 
FROM a
    INNER JOIN (SELECT b.* FROM b ORDER BY b.id DESC) b ON b.a_id = a.id
    LEFT JOIN c ON c.val1 = b.val1 AND c.val2 = b.val2
GROUP BY b.id

标签: mysqlgreatest-n-per-groupmysql-5.7

解决方案


您的描述是像 ROW_NUMBER() 这样的窗口函数有帮助的,但这些也是 MySQL 8+。在 MySQL 5.7 中,事情有点棘手,但有几种方法可以做到这一点。

SET @rank := 0;
SET @aid := 0;

SELECT
  a.*, b.*, c.*
FROM
  a
INNER JOIN (
    SELECT
      @rank := CASE WHEN b.a_id = @aid THEN @rank + 1 ELSE 1 END AS RankedRow
      ,b.*
      ,@aid := b.a_id
    FROM
      b
    ORDER BY
      b.a_id
      ,b.id DESC
) AS b
  ON a.id = b.a_id
  AND b.RankedRow = 1
LEFT JOIN c
  ON c.val1 = b.val1
  AND c.val2 = b.val2

这使用一个变量来保存当前行号,并使用一个技巧来更新该值并返回它。如果 a_id 相同,则增加 1,但当 a_id 更改时,它会重置为 1。

另一种选择是将 b 加入自身

SELECT
  a.*, b.*, c.*
FROM
  a
INNER JOIN (
  SELECT
    b.*
  FROM
    b
  LEFT JOIN b AS newerb
    ON newerb.a_id = b.a_id
    AND newerb.id > b.id
  WHERE
    newerb.id IS NULL
) AS b
  ON a.id = b.a_id
LEFT JOIN c
  ON c.val1 = b.val1
  AND c.val2 = b.val2

如果对于同一个 a_id 没有大于 b 的 id 并且因此必须是最新的行,这里 newerb 将为空。

我过去使用过这两种解决方案,我更喜欢后者,因为它更具可读性,但根据您拥有的数据量,它可能会更慢。

我没有时间测试这两个例子,所以对于我错过的任何错别字提前道歉。


推荐阅读