2010-04-25 75 views
2

我有兩個表,表A有700,000個條目,表B有600,000個條目。的結構如下:還有什麼我可以做的,以優化這個MySQL查詢?

表A:

+-----------+---------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+-----------+---------------------+------+-----+---------+----------------+ 
| id  | bigint(20) unsigned | NO | PRI | NULL | auto_increment | 
| number | bigint(20) unsigned | YES |  | NULL |    | 
+-----------+---------------------+------+-----+---------+----------------+ 

表B:

+-------------+---------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+-------------+---------------------+------+-----+---------+----------------+ 
| id   | bigint(20) unsigned | NO | PRI | NULL | auto_increment | 
| number_s | bigint(20) unsigned | YES | MUL | NULL |    | 
| number_e | bigint(20) unsigned | YES | MUL | NULL |    | 
| source  | varchar(50)   | YES |  | NULL |    | 
+-------------+---------------------+------+-----+---------+----------------+ 

我試圖找到,如果任何在表A中的值的使用存在於表B以下代碼:

$sql = "SELECT number from TableA"; 
$result = mysql_query($sql) or die(mysql_error()); 

while($row = mysql_fetch_assoc($result)) { 
     $number = $row['number']; 
     $sql = "SELECT source, count(source) FROM TableB WHERE number_s < $number AND number_e > $number GROUP BY source"; 
     $re = mysql_query($sql) or die(mysql_error); 
     while($ro = mysql_fetch_array($re)) { 
       echo $number."\t".$ro[0]."\t".$ro[1]."\n"; 
     } 
} 

我希望查詢會走得很快,但由於某種原因,它並不可怕快。我的選擇(與「數字」的特定值)解釋給我下面的:

mysql> explain SELECT source, count(source) FROM TableB WHERE number_s < 1812194440 AND number_e > 1812194440 GROUP BY source; 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys   | key | key_len | ref | rows | Extra          | 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | TableB  | ALL | number_s,number_e  | NULL | NULL | NULL | 696325 | Using where; Using temporary; Using filesort | 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
1 row in set (0.00 sec) 

有沒有我可以壓榨出這個任意優化?

我試着爲同一個任務編寫一個存儲過程,但它似乎並沒有在第一個地方工作......它沒有給出任何語法錯誤......我試着運行它一天,它仍然在運行,這感覺很奇怪。

CREATE PROCEDURE Filter() 
Begin 
    DECLARE number BIGINT UNSIGNED; 
    DECLARE x INT; 
    DECLARE done INT DEFAULT 0; 
    DECLARE cur1 CURSOR FOR SELECT number FROM TableA; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
    CREATE TEMPORARY TABLE IF NOT EXISTS Flags(number bigint unsigned, count int(11)); 
    OPEN cur1; 
    hist_loop: LOOP 
    FETCH cur1 INTO number; 
    SELECT count(*) from TableB WHERE number_s < number AND number_e > number INTO x; 
    IF done = 1 THEN 
     LEAVE hist_loop; 
    END IF; 
    IF x IS NOT NULL AND x>0 THEN 
     INSERT INTO Flags(number, count) VALUES(number, x); 
    END IF; 
    END LOOP hist_loop; 
    CLOSE cur1; 
END 
+2

讓我直截了當地說,你正在運行700,001個查詢,你驚訝它不快? – Thomas 2010-04-25 19:15:04

+0

嗯..我不是說它不快..我只是問是否還有更多的優化,我可以做得更快...... :) – Legend 2010-04-25 19:17:34

+0

如果你使用'$ number BETWEEN number_s和number_e'? – extraneon 2010-04-25 19:26:15

回答

4

您正在嘗試查找包含點的區間。 B-tree索引(大多數數據庫中的默認索引類型)的速度並不快,但是R-tree索引對於這類查詢會很好。 MySQL不允許直接更改索引的類型,但可以強制MySQL通過使用GEOMETRY列類型來使用R-Tree。

Quassnoi涵蓋在his article on nested sets in MySQL。雖然不完全相同,但非常相似。從文章引述:

還有一定的階級任務 那些需要搜索包含已知值的所有範圍 的:

* Searching for an IP address in the IP range ban list 
* Searching for a given date within a date range 

和其他幾個人。這些任務可以通過使用R-樹能力的MySQL

+0

非常感謝列表... R-Tree從來沒有出現過我...... – Legend 2010-04-25 19:28:41

+0

雖然這個答案很可能是這個問題的答案當有人使用類似'table1中的所有記錄的代碼時,我仍然會感到有趣的感覺做選擇。 ..從table2 where條件基於table1'的東西。一個聯合會恕我直言是一個更自然的思想方向。 – extraneon 2010-04-25 19:39:15

+0

@extraneon:的確,如果你閱讀這篇文章,你會發現這正是Quassnoi所說的,例如, 'JOIN t_hierarchy hrp ON MBRWithin(Point(0,hp.lft),hrp.sets)'。雖然在這裏你不需要所有的時間間隔 - 只要知道有一個就足夠了。 – 2010-04-25 19:43:44

2

它看起來像你對我對number_enumber_s列單獨的索引,可能與獨立ADD INDEX(number_e)ADD INDEX(number_s)列上創建。

如果添加一個包含這兩列的索引,因爲它們都在您的查詢中使用,並且MySQL顯然不選擇使用任何一列索引,您可能會獲得更好的性能,整個表掃描會更快(如果您的查詢跨越大範圍的值,這並不罕見)。

ALTER TABLE tblB ADD INDEX(number_s,number_e); 

你不會需要後,個人number_s指數,例如MySQL可以使用剛纔的數據庫查詢只針對number_s的人,所以你還不如丟棄之一。

+0

+1爲合併索引。沒有觀察到太大的差異,但我會繼續嘗試使用建議的R-Trees。謝謝! – Legend 2010-04-25 19:29:34

1

首先被 改善,我假定期望的輸出是到組中的所有「源」,其中輸入在於 number_e和number_s次,並且其計數之間。

我在語法沙啞,但你可以考慮使用低於/

編輯大於運營商

使用「之間」條款在那裏,而不是一個明確的比較:Zombat說什麼也適用;索引也會有所幫助。

+0

+1的建議。謝謝! – Legend 2010-04-25 19:24:38

相關問題