如何使用多个 JOIN 加速 SQL 查询?

Posted

技术标签:

【中文标题】如何使用多个 JOIN 加速 SQL 查询?【英文标题】:How to speed up SQL query with multiple JOINs? 【发布时间】:2020-08-21 23:37:16 【问题描述】:

以下 SQL 查询的执行时间为 8.0943 秒。有没有更好的方法来加快速度?

SELECT 
       e.idno, e.estatus,
       p.idno, p.id, p.time, p.date, p.employee, p.status, p.comment
       FROM e_company_data e
       INNER JOIN people_attendance p ON p.idno = e.idno
       WHERE p.id = (SELECT MAX(id) FROM people_attendance p1
                            WHERE p1.idno = p.idno)
       AND e.estatus = 1 ORDER BY e.idno

我已经索引了以下内容。

表:people_attendance 列:idno、日期、时间、员工、状态、评论

表:e_company_data 列:idno、状态

我可能在索引上做错了。任何帮助将不胜感激。谢谢。

(来自 pastebin)

CREATE TABLE `people_attendance` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `reference` int(11) DEFAULT NULL,
 `idno` varchar(11) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `date` date DEFAULT NULL,
 `employee` varchar(80) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `status` varchar(15) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `time` time DEFAULT NULL,
 `comment` varchar(80) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `reason` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL,
 `counter` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idxidno` (`idno`),
 KEY `idxattendance` (`employee`,`status`,`date`,`time`,`comment`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=12888 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

CREATE TABLE `e_company_data` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `reference` int(11) NOT NULL,
 `company` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `department` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'0\',
 `jobposition` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `companyemail` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `idno` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `pin` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `startdate` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `dateregularized` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `reason` varchar(455) COLLATE utf8mb4_unicode_ci DEFAULT \'\',
 `leaveprivilege` int(11) DEFAULT NULL,
 `estatus` int(2) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `idxcompdata` (`idno`,`department`,`estatus`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=130 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

【问题讨论】:

请提供SHOW CREATE TABLE。副手,我想你至少需要INDEX(status, idno) @RickJames 你可以在这里查看。 pastebin.com/embed_js/UZunSJ4s 谢谢。 【参考方案1】:

可能使用窗口函数:

SELECT e.idno, e.estatus,
       p.idno, p.id, p.time, p.date, p.employee, p.status, p.comment
FROM e_company_data e JOIN
     (SELECT p.*, ROW_NUMBER() OVER (PARTITION BY p.idno ORDER BY p.id DESC) as seqnum
      FROM people_attendance p
     ) p
     ON p.idno = e.idno AND seqnum = 1
WHERE e.estatus = 1
ORDER BY e.idno;

这应该受益于people_attendance(idno, id desc)e_company_data(status, idno) 上的索引。

编辑:

对于您的查询版本:

SELECT e.idno, e.estatus,
       p.idno, p.id, p.time, p.date, p.employee, p.status, p.comment
FROM e_company_data e JOIN
     people_attendance p
     ON p.idno = e.idno
WHERE p.id = (SELECT MAX(p2.id)
              FROM people_attendance p2
              WHERE p2.idno = p.idno
             ) AND
      e.estatus = 1
ORDER BY e.idno;

我会推荐e_company_data(status, idno)people_attendance(idno, id) 上的索引。

【讨论】:

谢谢@Gordon,但我收到了这个错误。 "#1064 - 您的 SQL 语法有错误;请查看与您的 mysql 服务器版本相对应的手册,以获取正确的语法,以便在 '(PARTITION BY p.idno ORDER BY p.id DESC) as seqnum FROM people_attendance' 附近使用4号线。”我正在使用 MySQL v5.7.29。这跟版本有关系吗? @xavier MySql 5.7 就现代数据库功能而言,还不如在 2006 年发布。您真的很想获得 8.0 版本。 @JoelCoehoorn 尽我所能,但我目前没有能力这样做,这是在共享托管服务器上实时运行的,但会在本地尝试。不过感谢您的建议。【参考方案2】:

试试这个:

SELECT  e.idno, e.estatus, p.idno, p.id, p.time, p.date, p.employee,
        p.status, p.comment
    FROM  ( SELECT idno, MAX(id) AS last_id
                 FROM people_attendance
                 GROUP BY idno ) AS x
    JOIN  e_company_data e  USING(idno)
    JOIN  people_attendance p  ON p.id = x.last_id
    WHERE  e.estatus = 1
    ORDER BY  e.idno

原理是将相关子查询变成派生表。它不是 130 个探针,而是对覆盖 INDEX(idno, id) 的一次快速扫描以获得 130 行。之后,剩下的就是高效的 JOIN。

另外,将INDEX(idno, status)(以任意顺序)添加到e_company_data

【讨论】:

谢谢。我得到了一个没有 GROUP BY 的非聚合列的错误。是否建议关闭警告信息?我在这里看到了一些答案:***.com/questions/34115174/… @xavier - 不,我搞砸了。请参阅我添加到派生表中的GROUP BY 哇!查询耗时 0.0244 秒。你是个传奇。非常感谢。【参考方案3】:

除了 Rick James 的回答之外,请记住,您的查询在聚合函数“SELECT MAX(id)”上很慢。考虑添加更新时会保留 max(id) 的字段。

【讨论】:

以上是关于如何使用多个 JOIN 加速 SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ChatGPT 编写 SQL JOIN 查询

SQL语句如何使用join?

如何在从多个表中获取多行的同时删除 sql JOIN 中的重复项

如何加快在 2500 万行上还包含 JOIN 的 SQL UPDATE

如何使用 CriteriaBuilder 的多个 JOIN 来构建 Predicate?

JOIN 中的 Sql 查询优化问题