2011-11-21 63 views
11

我研究过这一点,但我仍然无法解释为什么:为什么MySQL的查询,左连接“大大”比我内心快加入

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 

115ms对478ms。他们都使用InnoDB,并且定义了关系。 'card_legality'包含大约200k行,而'合法性'表包含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; 

我可以简单地用左加入,但它似乎并不完全正确......有什么想法吗?

更新: 根据要求,我已经包括每个解释的结果。我以前运行它,但我不假装有透彻的理解..

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 

及内部联接:

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 
+0

顺便说一下'card_id'是一个VARCHAR,因为我没有选择,我通常不会接受这一点。 – Ben

回答

9

这是因为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。另外参考文献是NULLCONST。不用说,第二个更好。

+0

aha!辉煌!你是对的,在'card_id'上搜索一个INT,实际上它是一个VARCHAR,这些查询使得这些查询比他们本来的要慢近600倍!谢谢Andreas :) – Ben

+0

是的。非常好的信息在这里谢谢Andreas。 – stefgosselin

+0

我已经添加了一个链接,在这里解释。这是很好的阅读。 –

3

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

+3

基于眼前的话题,我会说OP有很大的机会知道如何使用“EXPLAIN”。无论哪种方式,这种信息应该放在评论中,而不是在答案中,因为你不试图回答他的问题。 – Naatan

+0

嗨L2G,感谢您的评论。我会坚持** EXPLAIN **函数,看看我能找到什么。 mysql手册有点缺乏(或至少对我而言)。非常感谢 – Ben

0

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

ETA:快速侦察兵轮,我无法找到任何具体要坚持我的观点,从而.....

+0

谢谢K.Bob,我读过类似的文章,但喜欢你自己;发现没有证据。 – Ben

2

L2G有它几乎概括起来,虽然我怀疑这可能是因为VARCHAR的用于card_id的类型。

我实际上打印出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 | 
+--------------------------------+----------+ 

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

+0

这是非常有用的信息stefgosselin,谢谢。你的预测实际上是正确的,但安德烈亚斯的解释解释了它。感谢您的帮助:) – Ben