2011-11-21 60 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