如何使用多个 JOIN 加速 SQL 查询?
How to speed up SQL query with multiple JOINs?
下面的 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
我已经索引了以下内容。
Table: people_attendance
列:idno、日期、时间、员工、状态、评论
Table: e_company_data
列:idno,estatus
我可能在索引上做错了。任何帮助将不胜感激。谢谢
(来自 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
可能使用 window 函数:
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)
上的索引。
试一试:
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
原理是将关联子查询变成派生table。它不是 130 个探测,而是一次快速扫描覆盖 INDEX(idno, id)
以获得 130 行。之后,剩下的就是高效的JOIN了。
此外,将 INDEX(idno, status)
(按任意顺序)添加到 e_company_data
。
除了 Rick James 的回答之外,请记住您的查询在聚合函数 "SELECT MAX(id)" 上很慢。考虑添加更新时将保留 max(id) 的字段。
下面的 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
我已经索引了以下内容。
Table: people_attendance 列:idno、日期、时间、员工、状态、评论
Table: e_company_data 列:idno,estatus
我可能在索引上做错了。任何帮助将不胜感激。谢谢
(来自 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
可能使用 window 函数:
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)
上的索引。
试一试:
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
原理是将关联子查询变成派生table。它不是 130 个探测,而是一次快速扫描覆盖 INDEX(idno, id)
以获得 130 行。之后,剩下的就是高效的JOIN了。
此外,将 INDEX(idno, status)
(按任意顺序)添加到 e_company_data
。
除了 Rick James 的回答之外,请记住您的查询在聚合函数 "SELECT MAX(id)" 上很慢。考虑添加更新时将保留 max(id) 的字段。