为啥 mySQL 查询,左连接“明显”比我的内连接快

Posted

技术标签:

【中文标题】为啥 mySQL 查询,左连接“明显”比我的内连接快【英文标题】:Why is mySQL query, left join 'considerably' faster than my inner join为什么 mySQL 查询,左连接“明显”比我的内连接快 【发布时间】:2012-01-03 07:37:13 【问题描述】:

我对此进行了研究,但我仍然无法解释原因:

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = 23155

明显慢于:

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = 23155

115 毫秒与 478 毫秒。他们都使用 InnoDB 并且定义了关系。 “card_legality”包含大约 200k 行,而“legality”表包含 11 行。以下是每个的结构:

CREATE TABLE `card_legality` (
  `card_id` varchar(8) NOT NULL DEFAULT '',
  `legality_id` int(3) NOT NULL,
  `cl_boolean` tinyint(1) NOT NULL,
  PRIMARY KEY (`card_id`,`legality_id`),
  KEY `legality_id` (`legality_id`),
  CONSTRAINT `card_legality_ibfk_2` FOREIGN KEY (`legality_id`) REFERENCES `legality` (`legality_id`),
  CONSTRAINT `card_legality_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `card` (`card_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

还有:

CREATE TABLE `legality` (
  `legality_id` int(3) NOT NULL AUTO_INCREMENT,
  `l_name` varchar(16) NOT NULL DEFAULT '',
  PRIMARY KEY (`legality_id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;

我可以简单地使用 LEFT-JOIN,但它似乎不太正确……请问有什么想法吗?

更新: 根据要求,我已经包含了每个解释的结果。我之前也跑过,但我不假装对它有透彻的了解。..

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  cl  ALL PRIMARY NULL    NULL    NULL    199747  Using where
1   SIMPLE  l   eq_ref  PRIMARY PRIMARY 4   hexproof.co.uk.cl.legality_id   1   

AND,内连接:

id  select_type table   type    possible_keys   key key_len         ref                         rows    Extra
1   SIMPLE  l   ALL PRIMARY NULL    NULL    NULL    11  
1   SIMPLE  cl  ref PRIMARY,legality_id legality_id 4   hexproof.co.uk.l.legality_id    33799   Using where

【问题讨论】:

顺便说一句,card_id 是一个 VARCHAR,因为我别无选择,我通常不会接受。 【参考方案1】:

我会在这两个查询中尝试EXPLAIN。只需在每个 SELECT 前面加上 EXPLAIN 并运行它们。它提供了关于 mysql 如何优化和执行查询的非常有用的信息。

【讨论】:

根据手头的主题,我想说 OP 很有可能知道如何使用“EXPLAIN”。无论哪种方式,这种信息都应该放在评论中,而不是放在答案中,因为你没有试图回答他的问题。 您好 L2G,感谢您的评论。我会坚持使用 EXPLAIN 功能,看看我能找到什么。 mysql 手册有些欠缺(或者至少对我来说是这样)。非常感谢【参考方案2】:

这是因为card_id 上的varchar。 MySQL 不能将 card_id 上的索引用作 card_id,如此处所述mysql type conversion。重要的部分是

对于字符串列与数字的比较,MySQL 不能使用 列上的索引以快速查找值。如果 str_col 是 索引字符串列,执行时不能使用索引 在以下语句中查找:

SELECT * FROM tbl_name WHERE str_col=1;

这样做的原因是有许多不同的字符串可能 转换为值 1,例如“1”、“1”或“1a”。

如果您将查询更改为

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = '23155'

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = '23155'

您应该会看到速度的巨大提升,并且还会看到不同的解释。

这里有一个类似(但更简单)的测试来证明这一点:

> desc id_test;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | varchar(8) | NO   | PRI | NULL    |       |
+-------+------------+------+-----+---------+-------+
1 row in set (0.17 sec)

> select * from id_test;
+----+
| id |
+----+
| 1  |
| 2  |
| 3  |
| 4  |
| 5  |
| 6  |
| 7  |
| 8  |
| 9  |
+----+
9 rows in set (0.00 sec)

> explain select * from id_test where id = 1;
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | id_test | index | PRIMARY       | PRIMARY | 10      | NULL |    9 | Using where; Using index |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)


> explain select * from id_test where id = '1';
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | id_test | const | PRIMARY       | PRIMARY | 10      | const |    1 | Using index |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
1 row in set (0.00 sec)

第一种情况是Using where; Using index,第二种情况是Using index。 ref 也是NULLCONST。不用说,第二个更好。

【讨论】:

啊哈!杰出的!您是对的,将“card_id”作为 INT 进行搜索,而实际上它是 VARCHAR 使这些查询比应有的速度慢了近 600 倍!谢谢安德烈亚斯 :) 我添加了一个链接来解释这一点。读起来不错。【参考方案3】:

L2G 已经总结得差不多了,虽然我怀疑这可能是因为 card_id 使用了 varchar 类型。

我实际上打印了this informative page 用于基准测试/分析快速。这是一个快速的穷人分析技术:

Time a SQL on MySQL
Enable Profiling
mysql> SET PROFILING = 1
...
RUN your SQLs
...
mysql> SHOW PROFILES;

+----------+------------+-----------------------+
| Query_ID | Duration   | Query                 |
+----------+------------+-----------------------+
|        1 | 0.00014600 | SELECT DATABASE()     |
|        2 | 0.00024250 | select user from user |
+----------+------------+-----------------------+
mysql> SHOW PROFILE for QUERY 2;

+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000034 |
| checking query cache for query | 0.000033 |
| checking permissions           | 0.000006 |
| Opening tables                 | 0.000011 |
| init                           | 0.000013 |
| optimizing                     | 0.000004 |
| executing                      | 0.000011 |
| end                            | 0.000004 |
| query end                      | 0.000002 |
| freeing items                  | 0.000026 |
| logging slow query             | 0.000002 |
| cleaning up                    | 0.000003 |
+--------------------------------+----------+

祝你好运,哦,请发表你的发现!

【讨论】:

这是非常有用的信息 stefgosselin,谢谢。你的预测实际上是正确的,但安德烈亚斯的解释解释了这一点。感谢您的帮助:)【参考方案4】:

我很确定 MySql 对左连接有更好的优化 - 目前没有证据支持这一点。

预计到达时间:快速侦察一轮,我找不到任何具体的东西来支持我的观点,所以.....

【讨论】:

谢谢 K.Bob,我读过类似的,但喜欢你自己;找不到证据。

以上是关于为啥 mySQL 查询,左连接“明显”比我的内连接快的主要内容,如果未能解决你的问题,请参考以下文章

08_MySQL DQL(SQL99标准)_多表连接查询中的内连接

MySQL 的内连接、左连接、右连接有啥区别?

MySQL 表的内连和外连

MySQL的内连接,左连接,右连接,全连接

MySql表的内连和外连

MySQL中的内连接左连接右连接全连接交叉连接