2016-03-29 75 views
1

我使用MySQL和下面的背景:執行效率差異:一個SQL VS.兩個分裂SQL

mysql> show create table letter_index\G 
*************************** 1. row *************************** 
     Table: letter_index 
Create Table: CREATE TABLE `letter_index` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `owner` int(11) NOT NULL DEFAULT '0', 
    `target` int(11) NOT NULL DEFAULT '0', 
    `type` tinyint(4) unsigned NOT NULL DEFAULT '0', 
    `data_id` int(11) unsigned NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`), 
    KEY `target_idx` (`target`,`type`) 
) ENGINE=InnoDB 


mysql> show create table letter_data\G 
*************************** 1. row *************************** 
     Table: letter_data 
Create Table: CREATE TABLE `letter_data` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `topic` varchar(255) DEFAULT NULL, 
    `message` text NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB 

letter_index定義的字母關係:誰寫出來給誰接收,而表letter_data存儲信件內容的消息。

letter_indexdata_id列參考表letter_data主鍵id


我想得到某人的所有收到的消息。當我使用一個嵌套SQL來獲得結果時,它通常比兩個拆分SQL花費更多的時間。演示如下:

一個SQL:

mysql> select * from letter_data where id in (select data_id from letter_index where target=10718) 
    ... 
    ... # query result 
    ... 
78 rows in set (52.01 sec) 

斯普利特:

mysql> select data_id from letter_index where target=10718; 
    ... 
    ... # query result 
    ... 
78 rows in set (0.25 sec) 


mysql> select * from letter_data where id in (1,2,..`data_id result`..); 
    ... 
    ... # query result 
    ... 
78 rows in set (2.04 sec) 

效率差異是巨大的!

解釋相對SQLS並有一些提示:

mysql> explain select * from letter_data where id in (select data_id from letter_index where target=10718); 
+----+--------------------+--------------+------+---------------+------------+---------+-------+---------+-------------+ 
| id | select_type  | table  | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+--------------------+--------------+------+---------------+------------+---------+-------+---------+-------------+ 
| 1 | PRIMARY   | letter_data | ALL | NULL   | NULL  | NULL | NULL | 1103585 | Using where | 
| 2 | DEPENDENT SUBQUERY | letter_index | ref | target_idx | target_idx | 4  | const |  78 | Using where | 
+----+--------------------+--------------+------+---------------+------------+---------+-------+---------+-------------+ 


mysql> explain select data_id from letter_index where target=10718; 
+----+-------------+--------------+------+---------------+------------+---------+-------+------+-------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+--------------+------+---------------+------------+---------+-------+------+-------+ 
| 1 | SIMPLE  | letter_index | ref | target_idx | target_idx | 4  | const | 78 |  | 
+----+-------------+--------------+------+---------------+------------+---------+-------+------+-------+ 


mysql> explain select * from letter_data where id in (1,2,..`data_id result`..); 
+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+ 
| 1 | SIMPLE  | letter_data | range | PRIMARY  | PRIMARY | 4  | NULL | 78 | Using where | 
+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+ 

我的問題是,爲什麼嵌套SQL表letter_data possible_keys是NULL,而個人一個是PRIMARY

謝謝你的時間!

+0

爲什麼sql-server標記? – jarlh

+0

@jarlh對不起, – hedleyyan

+0

爲什麼不使用簡單的'INNER JOIN'?子查詢完全沒有意義,尤其是使用'IN'運算符。 – Mjh

回答

1

不知道如果我錯過了什麼,但爲什麼你不使用INNER JOIN爲:

SELECT 
    ld.* 
FROM 
    letter_data ld 
INNER JOIN letter_index li ON li.data_id = ld.id 
WHERE li.target = 10718 

結果將是相同的,最大的區別是,MySQL不會兌現任何子查詢。

+0

我明白了,有沒有我們更喜歡'in'到'inner join'的場景? – hedleyyan

+0

當你有一個來自外部的值列表(服務器端腳本,靜態列表等)時,通常使用'IN'。當你想通過其他表中的列表過濾數據時,最好使用'INNER JOIN'或'EXISTS/NOT EXISTS'。將IN用於子查詢時,數據庫首先處理整個子查詢,然後處理整個查詢,根據爲IN指定的關係進行匹配。使用EXISTS或JOIN,數據庫將在檢查指定的關係時返回true/false。除非子查詢中的表非常小,否則EXISTS或JOIN將比IN執行得更好。 – mitkosoft

+0

感謝您的耐心等待! – hedleyyan

1

表中的數據類型不同,導致類型轉換,從而阻止MySQL在第一個查詢中使用索引。 (letter_index.data_id未簽名,而letter_data.id已簽名。)這就是爲什麼MySQL在letter_data表上使用全表掃描的原因。

使用相同的數據類型,在data_id列創建一個索引,創建一個外鍵。

要提高查詢的性能,可以使用EXISTS而不是IN

解釋你的第二種方式:兩個查詢都是簡單的範圍或常量搜索,沒有任何困難,特別是當你在過濾的字段上有索引時。

+0

搞定!謝謝你的耐心! – hedleyyan