为什么LEFT JOIN比INNER JOIN慢?

all*_*hou 5 mysql join query-optimization

我有两个查询,第一个(内连接)超快,第二个(左连接)超慢.如何快速进行第二次查询?

EXPLAIN SELECT saved.email FROM saved INNER JOIN finished ON finished.email = saved.email;

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  finished    index   NULL    email   258 NULL    32168   Using index
1   SIMPLE  saved   ref email   email   383 func    1   Using where; Using index

EXPLAIN SELECT saved.email FROM saved LEFT JOIN finished ON finished.email = saved.email;

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  saved   index   NULL    email   383 NULL    40971   Using index
1   SIMPLE  finishedindex   NULL    email   258 NULL    32168   Using index
Run Code Online (Sandbox Code Playgroud)

编辑:我已在下面的两个表中添加了表格信息.

CREATE TABLE `saved` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `slug` varchar(255) DEFAULT NULL,
  `email` varchar(127) NOT NULL,
  [omitted fields include varchar, text, longtext, int],
  PRIMARY KEY (`id`),
  KEY `slug` (`slug`),
  KEY `email` (`email`)
) ENGINE=MyISAM AUTO_INCREMENT=56329 DEFAULT CHARSET=utf8;

CREATE TABLE `finished` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `slug` varchar(255) DEFAULT NULL,
  `submitted` int(11) DEFAULT NULL,
  `status` int(1) DEFAULT '0',
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  [omitted fields include varchar, text, longtext, int],
  PRIMARY KEY (`id`),
  KEY `assigned_user_id` (`assigned_user_id`),
  KEY `event_id` (`event_id`),
  KEY `slug` (`slug`),
  KEY `email` (`email`),
  KEY `city_id` (`city_id`),
  KEY `status` (`status`),
  KEY `recommend` (`recommend`),
  KEY `pending_user_id` (`pending_user_id`),
  KEY `submitted` (`submitted`)
) ENGINE=MyISAM AUTO_INCREMENT=33063 DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

Mar*_*ams 9

使用INNER JOIN,MySQL通常将从具有最小行数的表开始.在这种情况下,它从表开始,finished并在saved使用索引时查找相应的记录saved.email.

对于LEFT JOIN,(不包括一些优化)MySQL通常按顺序连接记录(从最左边的表开始).在这种情况下,MySQL从表开始saved,然后尝试查找每个相应的记录finished.由于没有可用的索引finished.email,因此必须对每个查找执行完整扫描.

编辑

现在,您张贴您的架构,我可以看到,MySQL是忽略指数(finished.email从去时)utf8latin1字符集.您没有为每列发布字符集和排序规则,因此我将使用表的默认字符集.排序规则必须兼容才能使MySQL使用索引.

MySQL可以强制(升级)一个latin1非常有限的utf8排序规则,例如unicode_ci(因此第一个查询可以saved.email通过升级latin1排序规则来使用索引utf8),但相反的情况并非如此(第二个查询不能使用)索引finished.email因为它无法将utf8排序规则降级到latin1).

解决方案是将两个电子邮件列更改为兼容的排序规则,最简单的方法是将它们设置为相同的字符集和排序规则.

  • 很好地捕捉了角色的差异. (3认同)
  • 字符设定点非常棒.我的查询从1小时到87毫秒 (2认同)

spe*_*593 9

LEFT JOIN查询是INNER JOIN查询,因为它做更多的工作.

从EXPLAIN输出看,MySQL看起来像是在进行嵌套循环连接.(嵌套循环没有任何问题;我认为这是MySQL在5.5及更早版本中使用的唯一连接操作.)

对于INNER JOIN查询,MySQL正在使用有效"ref"(索引查找)操作来定位匹配的行.

但对于LEFT JOIN查询,看起来MySQL正在对索引进行全面扫描以找到匹配的行.因此,通过嵌套循环连接操作,MySQL正在对另一个表中的每一行进行完整的索引扫描扫描.因此,这是数万次扫描的顺序,每次扫描都在检查数万行.

使用EXPLAIN输出中的估计行数,这将需要(40971*32168 =)1,317,955,128字符串比较.

INNER JOIN查询避免了大量的工作,因此速度更快.(它通过使用索引操作来避免所有这些字符串比较.

-- LEFT JOIN
id select table    type   key   key_len ref    rows  Extra
-- ------ -------- -----  ----- ------- ----  -----  ------------------------
1  SIMPLE saved    index  email     383 NULL  40971  Using index
1  SIMPLE finished index  email     258 NULL  32168  Using index

-- INNER JOIN 
id select table    type   key   key_len ref    rows  Extra
-- ------ -------- -----  ----- ------- ----  -----  ------------------------  
1  SIMPLE finished index  email     258 NULL  32168  Using index
1  SIMPLE saved    ref    email     383 func      1  Using where; Using index
                   ^^^^^                ^^^^  ^^^^^  ^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

注意: Markus Adams 发现了添加到您问题的emailCREATE TABLE语句中字符集的差异.

我相信,正在阻止MySQL为您的查询使用索引的字符集中的差异.


Q2:如何更快地进行LEFT JOIN查询?

答:我不相信可以让特定的查询更快地运行,而无需更改架构,例如更改两个电子邮件列的字符集以匹配.

finished表的"外部联接" 看起来唯一的影响就是在找到多个匹配行时生成"重复"行.我不明白为什么需要外连接.为什么不完全摆脱它,只是做:

SELECT saved.email FROM saved
Run Code Online (Sandbox Code Playgroud)