MySql缓慢查询一个连接和两个表上的条件

时间:2017-06-11 12:22:29

标签: mysql performance inner-join

我无法理解为什么以下查询如此缓慢:

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
);

2 个答案:

答案 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的列。他们只是将您的数据分成两半,优化器通常会选择这样的索引,即使对于第一列不存在的查询也是如此。

到解释部分。在单个表上运行查询时,数据库引擎只读取所有记录并过滤掉那些不匹配的记录。它很慢,但是对于合理的少量记录,你不会知道它们之间的区别 但是当你加入另一个表时,它必须读取一个表中的所有行,并且对于每一行在第二个表中查找。但是因为你没有索引,它必须从第一个表中读取每个记录的第二个表。因此,当在第二个表中按行对照单个表运行查询时,花费在查询上的时间会增加,并且您得到的结果是时间 实际上并非那么简单。这只是简化的例子。