[英]MySQL Extremely Slow Query
I am trying to learn about optimizations to MySQL, table engines and when to use them, etc. 我正在尝试学习对MySQL,表引擎以及何时使用它们等的优化。
I have a query that is running up against the time-out limit of 10 minutes and which needs to complete in seconds because its function is a user-generated report. 我有一个查询,该查询的超时限制为10分钟,由于它的功能是用户生成的报告,因此需要在几秒钟内完成。
The Query: 查询:
SELECT em.employeeId, tsk.taskId
FROM employee em INNER JOIN
task tsk
ON tsk.employeeId = em.employeeId
WHERE em.employeeId <> 'Not Done'
AND tsk.employeeId (
SELECT employeeId FROM task
WHERE templateId
IN ( '5', '6', '7', '8' )
AND tsk.status = 'Done'
)
AND tsk.employeeId IN
(
SELECT employeeId FROM task
WHERE templateId IN
( '55', '56', '57', '58' )
AND status = 'Not Done'
)
Explain: 说明:
# id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, PRIMARY, tsk, ALL, , , , , 61326, Using where
1, PRIMARY, em, eq_ref, PRIMARY, PRIMARY, 4, newhire.tsk.employeeId, 1, Using index
3, DEPENDENT SUBQUERY, task, ALL, , , , , 61326, Using where
2, DEPENDENT SUBQUERY, task, ALL, , , , , 61326, Using where
The DB server uses MyISAM as default, so most schemas including this one are MyISAM. DB服务器默认使用MyISAM,因此包括该模式在内的大多数模式都是MyISAM。
I also realize that the text searches ( status=Done
or status LIKE 'Done'
) are adding a lot to the query. 我还意识到,文本搜索(
status=Done
或status LIKE 'Done'
)给查询增加了很多。
EDIT1: 编辑1:
# Table, Create Table
employee, CREATE TABLE `employee` (
`employeeId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) NOT NULL,
`firstName` varchar(255) NOT NULL,
`applicantId` varchar(255) NOT NULL,
`fEmployeeId` varchar(255) DEFAULT NULL,
`opId` varchar(255) DEFAULT NULL,
`rehire` tinyint(3) unsigned NOT NULL DEFAULT '0',
`sDate` date DEFAULT NULL,
`oDate` date DEFAULT NULL,
`cDate` date DEFAULT NULL,
`additionalDate` date DEFAULT NULL,
`additionalType` varchar(255) DEFAULT NULL,
`processingDate` date DEFAULT NULL,
`created` datetime NOT NULL,
`recruiterId` int(10) unsigned NOT NULL,
`processorId` int(10) unsigned DEFAULT NULL,
`position` tinyint(3) unsigned NOT NULL DEFAULT '1',
`status` varchar(255) NOT NULL,
`campus` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`requisition` varchar(255) DEFAULT NULL,
`Position` varchar(255) DEFAULT NULL,
`department` varchar(255) DEFAULT NULL,
`jobClass` varchar(255) DEFAULT NULL,
`hiringManager` varchar(255) DEFAULT NULL,
`badge` varchar(255) DEFAULT NULL,
`currentAddress` varchar(255) DEFAULT NULL,
`holding` tinyint(3) unsigned DEFAULT '0',
PRIMARY KEY (`employeeId`)
) ENGINE=MyISAM AUTO_INCREMENT=3959 DEFAULT CHARSET=latin1
EDIT 2: 编辑2:
# Table, Create Table
task, CREATE TABLE `task` (
`taskId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`templateId` int(10) unsigned NOT NULL,
`employeeId` int(10) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`naAvailable` tinyint(3) unsigned DEFAULT '0',
`fileRequired` tinyint(3) unsigned DEFAULT '0',
`fileHrCatalog` int(10) unsigned DEFAULT NULL,
`quickFileName` varchar(255) DEFAULT NULL,
`fileUploaded` tinyint(3) unsigned DEFAULT '0',
`fileExt` varchar(255) DEFAULT NULL,
`level` tinyint(3) unsigned NOT NULL,
`status` varchar(255) NOT NULL,
`due` date DEFAULT NULL,
`daysDue` int(10) unsigned DEFAULT NULL,
`routeIncentives` tinyint(3) unsigned DEFAULT '0',
`requiresAudit` tinyint(3) unsigned DEFAULT '0',
`auditStatus` varchar(255) DEFAULT NULL,
`auditUser` int(10) unsigned DEFAULT NULL,
`auditDate` datetime DEFAULT NULL,
`stampOption` tinyint(3) unsigned DEFAULT '0',
`done` tinyint(3) unsigned DEFAULT '0',
`doneBy` int(10) unsigned DEFAULT NULL,
`doneWhen` datetime DEFAULT NULL,
`sortOrder` tinyint(3) unsigned NOT NULL DEFAULT '255',
PRIMARY KEY (`taskId`),
KEY `status` (`status`,`templateId`)
) ENGINE=MyISAM AUTO_INCREMENT=176802 DEFAULT CHARSET=latin1
I would write the query as below, but to help the optimization, have a covering indexes on your tables. 我将按以下方式编写查询,但是为了帮助优化,表上应包含覆盖索引。
Employee table -- index on ( status, employeeID ) Task table -- index on ( employeeid, templateid, status ) 员工表-(状态,employeeID)上的索引任务表-(employeeid,templateid,状态)上的索引
By the first join, you are prequalifying to get the first task as a "Done" status. 通过首次加入,您将有资格获得第一个任务为“完成”状态。
The second join is looking for the OTHER task you are interested in that is NOT Done. 第二个联接正在寻找您感兴趣的其他任务“未完成”。
Doing subqueries (especially correlated sub queries) can be harder on performance. 进行子查询(尤其是相关子查询)可能会提高性能。 By doing a JOIN, it's either there or its not...
通过执行JOIN,它要么在那里,要么不...
SELECT
em.employeeId,
tsk.taskId
FROM
employee em
INNER JOIN task tsk1
ON em.employeeId = tsk1.employeeId
AND tsk1.templateID in ( '5', '6', '7', '8' )
AND tsk1.status = 'Done'
INNER JOIN task tsk2
ON em.employeeId = tsk2.employeeId
AND tsk2.templateID in ( '55', '56', '57', '58' )
AND tsk2.status = 'Not Done'
WHERE
em.status <> 'Not Done'
Your first change should be to create an index on task
that covers both the status
and templateId
columns: 您的第一个更改应该是在
task
上创建一个索引,该索引同时包含status
和templateId
列:
ALTER TABLE task ADD INDEX (status, templateId);
That'll prevent the full-table scans of 61326 rows each time that table is accessed in your query. 这样可以防止在查询中每次访问该表时对61326行进行全表扫描。
Also, it looks like you might have made a typo here: 另外,您似乎在这里输入过错误:
SELECT employeeId FROM task
WHERE templateId
IN ( '5', '6', '7', '8' )
AND tsk.status = 'Done'
That tsk.status
should be just status
like the 2nd subquery. 该
tsk.status
应该像第二tsk.status
查询一样处于status
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.