如何在 MySQL 5.7 中执行相当于横向连接的操作?
How to do the equivalent of a lateral join in MySQL 5.7?
我正在尝试从两个 table 中获取信息,一个 table 包含关于对象 A 的一般数据,而 B 保存该对象的补充数据的历史记录。
我想做的是通过快速请求从 A 和 B 的相应最新实例(最高 id)获取所有记录,这两个 table 相当大。 B 有一个密钥,允许它识别它链接到哪个 A,因为它是一对多关系。
如果有来自第三个 table 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
您所描述的是 window 功能,如 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
这使用一个变量来保存当前行号,并使用一个技巧来更新该值以及 return 它。如果 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 将为空,因此必须是最新的行。
我过去使用过这两种解决方案,我更喜欢后者,因为它更具可读性,但它可能会更慢,具体取决于您拥有的数据量。
我没有时间测试这两个示例,因此对于我遗漏的任何拼写错误提前表示歉意。
我正在尝试从两个 table 中获取信息,一个 table 包含关于对象 A 的一般数据,而 B 保存该对象的补充数据的历史记录。
我想做的是通过快速请求从 A 和 B 的相应最新实例(最高 id)获取所有记录,这两个 table 相当大。 B 有一个密钥,允许它识别它链接到哪个 A,因为它是一对多关系。 如果有来自第三个 table 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
您所描述的是 window 功能,如 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
这使用一个变量来保存当前行号,并使用一个技巧来更新该值以及 return 它。如果 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 将为空,因此必须是最新的行。
我过去使用过这两种解决方案,我更喜欢后者,因为它更具可读性,但它可能会更慢,具体取决于您拥有的数据量。
我没有时间测试这两个示例,因此对于我遗漏的任何拼写错误提前表示歉意。