我无法理解为什么以下查询如此缓慢:
select er.Id
from employeeRequests er
inner join employees e on e.id = er.idEmployee
where er.DateTime1 >= '2017-03-11'
and er.DateTime1 <= '2018-06-10'
and er.state = 0 and e.idCompany = 37;
这两个表都是InnoDB。 'employeerequests'表有200,000条记录。 '员工'表有10.000。 在快速机器上,查询需要大约4秒钟才能执行。
EXPLAIN返回以下行:
select_type table type possible_keys key key_len ref rows filtered Extra
SIMPLE e ref PRIMARY,FK_employee_idCompany_idx FK_employee_idCompany_idx 8 const 211 100.00 Using index
SIMPLE er ref FK_employeeRequest_IdEmployee_idx FK_employeeRequest_IdEmployee_idx 8 db.e.id 77 1.11 Using where
据我所知,第二行“过滤= 1.11”可能是问题所在,但我不知道如何处理它。
如果我删除连接并用“er.idEmployee in(1,2,...)”之类的条件替换它,那么查询变得非常快,但是我不喜欢这个解决方案,无论如何,我会想了解为什么我不能通过加入获得相同的结果。
这些是包含所有相关字段的CREATE TABLE语句:
CREATE TABLE `employeerequests` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dateTime1` datetime NOT NULL DEFAULT '1899-12-31 00:00:00',
`idEmployee` bigint(20) NOT NULL DEFAULT '0',
`state` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `FK_employeeRequest_IdEmployee_idx` (`idEmployee`),
CONSTRAINT `FK_employeeRequest_IdEmployee` FOREIGN KEY (`idEmployee`) REFERENCES `employees` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE `employees` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`idCompany` bigint(20) NOT NULL DEFAULT '0',
`firstName` varchar(100) NOT NULL DEFAULT '',
`lastName` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `FK_employee_idCompany_idx` (`idCompany`),
CONSTRAINT `FK_employee_idCompany` FOREIGN KEY (`idCompany`) REFERENCES `companies` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
);
答案 0 :(得分:0)
尝试此查询:
select er.id
from employeerequests er
where er.datetime1 between '2017-03-11' and '2018-06-10'
and er.state = 0
and exists (select 1 from employees where idcompany = 37 and id = er.idemployee);
您可以通过创建如下所示的复合索引来增加速度:
create index idx_employeerequests_dt_st
on employeerequests (datetime1, state);
create index idx_employees_id_company
on employees (id, idcompany);
由于您需要从employeerequests
获取ID,因此查询主要是查看该表并过滤表。 Exists
检查employees
主表中是否存在该特定员工,该表由所选公司过滤。这两个表现在都在对缩减/过滤的数据集进行比较,这可能有助于提高速度。
粗略的解释/猜测
原始查询就像拿两叠纸一样。从两个堆栈中挑选匹配的纸张并创建一堆新文件。该堆栈可能有许多匹配的文件。从这个新的许多论文堆栈中,选择仅过滤那些与日期范围,公司和州相匹配的论文。这是您需要的数据。问题可能是您从大型第3堆栈中过滤。
修改后的查询正在查看第一个堆栈(employeerequests
),并且只选择具有固定日期范围内某个状态的论文。这是一小部分论文。在拿起每篇论文时,您需要检查另一个论坛(employees
),看看所选论文是否具有来自employees
堆栈的employeeid和公司匹配。如果是,请选择该数据。因此,修改后的查询只是查看过滤产生的较小数据堆栈。再一次,这是一种猜测。
确认自己
您应该对原始查询和上面的查询执行EXPLAIN select ...
,以查看MySQL为索引选择的内容以及过滤的效果。
替代查询
你也可以试一试,看看你得到了什么结果:
select er.Id
from employeeRequests er
inner join employees e on
e.id = er.idEmployee
and er.DateTime1 between '2017-03-11' and '2018-06-10'
and er.state = 0
and e.idCompany = 37;
答案 1 :(得分:0)
尝试这两个索引。
create index idx_id_company
on employees (idcompany);
create index idx_employeerequests_emp_st_dt
on employeerequests (idEmployee, state, DateTime1);
第一个将加速idcompany对员工的搜索。第二个将获取员工ID,并将深入了解员工请求,然后按州和DateTime1过滤掉。
您可能会尝试在第二个索引中切换state和idEmployee,看看它是否有助于加快速度
不要试图聪明并将DateTime1放在索引的开头。它会伤害你的查询。
与一般建议相反,在为复合查询创建索引时,最好将第一列放在选择性不太好的位置。我的最爱是1/0的列。他们只是将您的数据分成两半,优化器通常会选择这样的索引,即使对于第一列不存在的查询也是如此。
到解释部分。在单个表上运行查询时,数据库引擎只读取所有记录并过滤掉那些不匹配的记录。它很慢,但是对于合理的少量记录,你不会知道它们之间的区别 但是当你加入另一个表时,它必须读取一个表中的所有行,并且对于每一行在第二个表中查找。但是因为你没有索引,它必须从第一个表中读取每个记录的第二个表。因此,当在第二个表中按行对照单个表运行查询时,花费在查询上的时间会增加,并且您得到的结果是时间 实际上并非那么简单。这只是简化的例子。