中小型表上的左连接非常慢

Posted

技术标签:

【中文标题】中小型表上的左连接非常慢【英文标题】:Very slow Left Join on small/medium tables 【发布时间】:2015-07-31 15:08:57 【问题描述】:

我遇到了一个问题,即某个特定的左连接大大减慢了重要查询的速度。使用 phpMyAdmin 测试查询,它指出查询需要 3.9 秒才能返回 546 行,但我不得不等待 67 秒才能看到结果,这让我觉得很奇怪。

涉及两个表: nsi(1,553 行)和文件(233,561 行)。此查询中提到的所有列都被单独索引,并且文件表中的 filejobid、category 和 isactive 上有一个复合索引。被比较的所有东西也是一个整数。

这个查询的淡化版本的目标是显示 nsi 表中的行一次,并能够确定是否有人上传了类别 20 的文件。可以有多个文件,但应该只有一行,因此是分组。

查询:

SELECT 
    nsi.id AS id,
    f.id AS filein
FROM nsi nsi
LEFT JOIN files f
    ON f.filejobid=nsi.leadid AND f.category=20 AND f.isactive=1
WHERE nsi.isactive=1
GROUP BY nsi.id

此数据的 67 秒加载时间对于我的应用程序来说简直是不可接受的,我不知道如何进一步优化它。我已经索引和复合索引。我是否应该只考虑一个新的更迂回的解决方案?

感谢您的帮助!

【问题讨论】:

你只需要一个索引,如果它不好,查询优化器会选择一个更好的。您应该删除每列上不必要的开销的单个索引。加上删除该复合索引。当我确定它是 1 或 0 时使用 isactive ,因此两种可能的情况会抓取您的整个数据集而不是分区。基于这个淡化的查询,我会将您的索引仅放在 nsi.id 上。 我不知道你认为你想做什么。如果您愿意,请考虑遵循这个简单的两步操作: 1. 如果您还没有这样做,请提供适当的 DDL(和/或 sqlfiddle),以便我们可以更轻松地复制问题。 2. 如果您尚未这样做,请提供与步骤 1 中提供的信息相对应的所需结果集。 FirebladeDan 实际上完全按照我的需要解决了它。删除化合物和 isactive 索引大大加快了它的速度。我没有考虑过会以这种方式使用更多索引来为 SQL 创建更多工作。有趣的东西。谢谢你,FirebladeDan! 是的没问题的首席 【参考方案1】:

这是您的查询,我觉得这有点可疑,因为您在 f.id 上有聚合但没有聚合功能。它将返回一个任意匹配的 id:

SELECT nsi.id AS id, f.id AS filein
FROM nsi LEFT JOIN
     files f
     ON f.filejobid = nsi.leadid AND f.category = 20 AND f.isactive = 1
WHERE nsi.isactive = 1
GROUP BY nsi.id;

对于这个查询,我认为最好的索引是files(filejobid), category, isactive)nsi(isactive, filejobid, id)

但是您可以轻松地重写查询以提高效率,因为它不需要 group by(假设 nsi.id 是唯一的):

SELECT nsi.id AS id,
       (SELECT f.id
        FROM files f
        WHERE  f.filejobid = nsi.leadid AND f.category = 20 AND f.isactive = 1
        LIMIT 1
       ) AS filein
FROM nsi 
WHERE nsi.isactive = 1;

同样的索引也适用于此。

如果您想要一个匹配文件的列表,而不仅仅是一个,请在任一查询中使用 group_concat()

【讨论】:

以上是关于中小型表上的左连接非常慢的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL:与 Group_Concatenate 一起使用的两个表上的左连接

小表上的仅索引扫描非常慢

有很多行的表上的基本选择非常慢

大表上的第一次查询调用速度非常慢

在 Entity Framework Core 中,如何使用数据库级别的条件(不是本地 LINQ)查询连接表上的数据?

InnoDB Small 更新很慢,性能问题