MYSQL Left join 在索引列上非常慢

Use*_*384 5 mysql sql

以下是4张表的表结构:

日历:

CREATE TABLE `calender` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `HospitalID` int(11) NOT NULL,
  `ColorCode` int(11) DEFAULT NULL,
  `RecurrID` int(11) NOT NULL,
  `IsActive` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID_UNIQUE` (`ID`),
  KEY `idxHospital` (`ID`,`StaffID`,`HospitalID`,`ColorCode`,`RecurrID`,`IsActive`)
) ENGINE=InnoDB AUTO_INCREMENT=4638 DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

日历参加者:

CREATE TABLE `calenderattendee` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `CalenderID` int(11) NOT NULL,
  `StaffID` int(11) NOT NULL,
  `IsActive` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`ID`),
  KEY `idxCalStaffID` (`StaffID`,`CalenderID`)
) ENGINE=InnoDB AUTO_INCREMENT=20436 DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

呼叫计划员工:

CREATE TABLE `callplanstaff` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Staffname` varchar(45) NOT NULL,
  `IsActive` tinyint(4) NOT NULL DEFAULT '1',
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID_UNIQUE` (`ID`),
  KEY `idx_IsActive` (`Staffname`,`IsActive`),
  KEY `idx_staffName` (`Staffname`,`ID`) USING BTREE KEY_BLOCK_SIZE=100
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

用户:

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL DEFAULT '',
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_users_on_email` (`email`),
  UNIQUE KEY `index_users_on_name` (`name`),
  KEY `idx_email` (`email`) USING BTREE KEY_BLOCK_SIZE=100
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

我想要做的是使用以下查询获取 calender.ID 和 Users.name:

SELECT a.ID, h.name
    FROM `stjude`.`calender` a 
    left join calenderattendee e on a.ID = e.calenderID
    left join callplanstaff f on e.StaffID = f.ID
    left join users h on f.Staffname = h.email
Run Code Online (Sandbox Code Playgroud)

这些表之间的关系是:

在此处输入图片说明

获取 13000 条记录大约需要 4 秒,我敢打赌它可能会更快。

当我查看查询的表格解释时,结果如下:

在此处输入图片说明

为什么 MYSQL 不在 callplanstaff 表和用户表上使用索引?

另外,就我而言,我应该使用多索引而不是多列索引吗?

是否有任何我遗漏的索引,所以我的查询很慢?

================================================== ======================

更新:

由于 zedfoxus 和 spencer7593 建议更改 idxCalStaffID 的排序和 idx_staffname 的排序,以下是执行计划:

在此处输入图片说明

获取需要 0.063 秒,所需时间少得多,索引的顺序如何影响获取时间..?

Bil*_*win 8

您误解了 EXPLAIN 报告。

  • type: index不是什么好事。这意味着它正在执行“索引扫描”,检查索引的每个元素。它几乎和表扫描一样糟糕。注意列rows: 4562rows: 13451。这是它将为每个表检查的索引元素的估计数量。
  • 两个表进行索引扫描甚至更糟。为此连接检查的总行数为 4562 x 13451 = 61,363,462。
  • Using join buffer不是什么好事。当优化器不能使用索引进行连接时,这是一种安慰。
  • type: eqref是件好事。这意味着它使用 PRIMARY KEY 索引或 UNIQUE KEY 索引来精确查找一行。注意列rows: 1。因此,至少对于前一个连接中的每一行,它只执行一次索引查找。

  • 您应该按照该顺序在 calenderattendee 上为列(CalenderId、StaffId)创建一个索引(@spencer7593 在我写帖子时发布了这个建议)。

  • 通过LEFT [OUTER] JOIN在此查询中使用,您可以防止 MySQL 优化表连接的顺序。并且由于您的查询 fetches h.name,我推断您真的只想要日历事件有参加者并且参加者有相应用户记录的结果。您不使用INNER JOIN.

这是新索引的 EXPLAIN,连接更改为INNER JOIN(尽管我的行数没有意义,因为我没有创建测试数据):

+----+-------------+-------+------------+--------+--------------------------------+----------------------+---------+----------------+------+----------+-----------------------+
| id | select_type | table | partitions | type   | possible_keys                  | key                  | key_len | ref            | rows | filtered | Extra                 |
+----+-------------+-------+------------+--------+--------------------------------+----------------------+---------+----------------+------+----------+-----------------------+
|  1 | SIMPLE      | a     | NULL       | index  | PRIMARY,ID_UNIQUE,idxHospital  | ID_UNIQUE            | 4       | NULL           |    1 |   100.00 | Using index           |
|  1 | SIMPLE      | e     | NULL       | ref    | idxCalStaffID,CalenderID       | CalenderID           | 4       | test.a.ID      |    1 |   100.00 | Using index           |
|  1 | SIMPLE      | f     | NULL       | eq_ref | PRIMARY,ID_UNIQUE              | PRIMARY              | 4       | test.e.StaffID |    1 |   100.00 | NULL                  |
|  1 | SIMPLE      | h     | NULL       | eq_ref | index_users_on_email,idx_email | index_users_on_email | 767     | func           |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+--------+--------------------------------+----------------------+---------+----------------+------+----------+-----------------------+
Run Code Online (Sandbox Code Playgroud)

所述type: index用于calenderattendee表已被更改为type: ref这意味着对非唯一索引的索引查找。并且关于的注释Using join buffer消失了。

那应该运行得更好。


索引的顺序如何影响获取时间..?

想一想电话簿,它先按姓氏排序,然后按名字排序。这可以帮助您非常快速地按姓氏查找人员。但它不能帮助您按名字查找人员。

索引中列的位置很重要!

您可能会喜欢我的演示文稿如何设计索引,真的

  • 另一个很好的答案,正如我们通常对比尔的期望。+10。解释 MySQL EXPLAIN 输出的好处,以及不必要的冗余索引。 (2认同)