2015-06-06 19 views
4

最快的方式,我有3個表看起來像這樣:什麼是加入幾個表匹配特定的列值在MySQL

CREATE TABLE big_table_1 (
id INT(11), 
col1 TINYINT(1), 
col2 TINYINT(1), 
col3 TINYINT(1), 
PRIMARY KEY (`id`) 
) 

等了big_table_2和big_table_3。 col1,col2,col3值爲0,1或null。

我在找每個表中col1值等於1的id's。我加入他們的行列如下,用我能想到的最簡單的方法:

SELECT t1.id 
FROM big_table_1 AS t1 
INNER JOIN big_table_2 AS t2 ON t2.id = t1.id 
INNER JOIN big_table_3 AS t3 ON t3.id = t1.id 
WHERE t1.col1 = 1 
AND t2.col1 = 1 
AND t3.col1 = 1; 

由於每個臺10萬行,查詢需要約40秒到我的機器上執行:

407231 rows in set (37.19 sec) 

解釋結果:

+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows  | Extra  | 
+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+ 
| 1 | SIMPLE  | t3 | ALL | PRIMARY  | NULL | NULL | NULL   | 10999387 | Using where | 
| 1 | SIMPLE  | t1 | eq_ref | PRIMARY  | PRIMARY | 4  | testDB.t3.id |  1 | Using where | 
| 1 | SIMPLE  | t2 | eq_ref | PRIMARY  | PRIMARY | 4  | testDB.t3.id |  1 | Using where | 
+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+ 

如果我在col1申報指標,其結果是稍微慢一點:

407231 rows in set (40.84 sec) 

我也曾嘗試以下查詢:

SELECT t1.id 
FROM (SELECT distinct ta1.id FROM big_table_1 ta1 WHERE ta1.col1=1) as t1 
WHERE EXISTS (SELECT ta2.id FROM big_table_2 ta2 WHERE ta2.col1=1 AND ta2.id = t1.id) 
AND EXISTS (SELECT ta3.id FROM big_table_3 ta3 WHERE ta3.col1=1 AND ta3.id = t1.id); 

但它的速度較慢:

407231 rows in set (44.01 sec) [with index on col1] 
407231 rows in set (1 min 36.52 sec) [without index on col1] 

是上述簡單的方法基本上是在MySQL這樣做的最快方法?爲了更快地獲得結果,是否有必要將表分成多個服務器?

附錄:根據要求解釋安德魯的代碼的結果(我修剪表下降到只有1萬行,而該指數是對ID和COL1):

+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref | rows | Extra       | 
+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+ 
| 1 | PRIMARY  | <derived3> | ALL | NULL   | NULL | NULL | NULL | 332814 |        | 
| 1 | PRIMARY  | <derived4> | ALL | NULL   | NULL | NULL | NULL | 333237 | Using where; Using join buffer | 
| 1 | PRIMARY  | <derived2> | ALL | NULL   | NULL | NULL | NULL | 333505 | Using where; Using join buffer | 
| 4 | DERIVED  | big_table_3 | index | NULL   | PRIMARY | 5  | NULL | 1000932 | Using where; Using index  | 
| 3 | DERIVED  | big_table_2 | index | NULL   | PRIMARY | 5  | NULL | 1000507 | Using where; Using index  | 
| 2 | DERIVED  | big_table_1 | index | NULL   | PRIMARY | 5  | NULL | 1000932 | Using where; Using index  | 
+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+ 

回答

1

踢嘗試與覆蓋索引(由id,col1組成) 所以1個索引使其成爲主要組合。沒有其他索引。

然後運行analyze table XXX(共3次,每一次表)

那麼火而過希望MySQL的CBO心不是成緻密來弄明白。

第二個想法是查看沒有where子句的結果。轉換其加入的條款

+0

結果是覆蓋索引快4倍,很好。這將是一個問題,如果我有更多的cols,並不斷選擇不同的。轉換爲連接的速度與使用'where'的速度大致相同。 –

+0

Thx讓我們知道 – AsConfused

+0

如果你可以忍受一個稍微慢點的插入或者更新(在colN列上),並且這是一個頻率需求值得它面對「新數據」慢......然後幾個覆蓋索引索引中涵蓋了所有信息,非常窄(大小)的列組合將使引擎不必前往數據頁面。如果需要的話,如果mysql cbo有腦屁,使用'force index' – AsConfused

0

的所有內你有沒有試過這樣:

SELECT t1.id 
FROM 
(SELECT id from big_table_1 where col1 = 1) AS t1 
INNER JOIN (SELECT id from big_table_2 where col1 = 1) AS t2 ON t2.id = t1.id 
INNER JOIN (SELECT id from big_table_3 where col1 = 1) AS t3 ON t3.id = t1.id 
+0

它運行了幾分鐘而沒有結果,所以我取消了它。 –

+0

這是沒有用的,因爲它需要創建一個子查詢。 –

+0

是的,但子查詢只提取ID的;如果有數百個列,這應該會更快......我想知道查詢計劃是什麼。 – Andrew

0

INNER JOIN(同JOIN)讓優化挑是否使用該表的左邊或表其右。您提供的簡化的SELECT可以從三個表中的任何一個開始。

優化器喜歡從帶有WHERE子句的表開始。你的簡單例子意味着每張桌子都是同樣好的如果有一個INDEXcol1開始。 (請參閱下面的收回。)

第二和後續表格需要一個不同的索引規則。在您的簡化示例中,col1用於過濾,id用於JOINingINDEX(col1, id)INDEX(id, col1)同樣適用於第二張桌子。

我一直在說「你的簡單例子」,因爲只要你改變了任何東西,這些答案中的大部分建議都值得一試。

(縮進)如果您有一個具有「低基數」的列,例如col%,只有0,1和NULL的可能性,INDEX(col1)本質上是無用的,因爲它更快地盲掃表而不是使用索引。

另一方面,INDEX(col1, ...)可能是有用的,正如第二個表所述。

然而,對於第一個表也不是有用的。如果你有這樣的INDEX,它將被忽略。

然後來「覆蓋」。再一次,你的例子是不切實際的簡單化,因爲除了idcol1之外基本上沒有觸及的字段。 「覆蓋」索引包括全部查詢中接觸的表的字段。覆蓋指標實際上總是小於數據,因此覆蓋指標的運行費用較低,因此速度更快。

(縮回的縮回)INDEX(col1, id),在該爲了對於第一表有用的覆蓋索引。

想象一下,如果您沒有提到col1只有3個值,我的討論就會如何消失。很不一樣。

而且我們還沒有得到到ORDER BYIN(...)BETWEEN...AND...,發動機的差異,技巧與PRIMARY KEYLEFT JOIN

More insight into building indexes from Selects

ANALYZE TABLE應該沒有必要。