6

我正在玩弄(感興趣),用簡單鄰接列表中的節點樹檢索使用局部變量的遞歸查詢。使用不使用INDEX的查詢變量進行SELECT選擇

我迄今爲止的解決方案很有趣,但我想知道爲什麼MySQL拒絕使用任何INDEX來優化此查詢。 MySQL不能通過使用INDEX來查找最近的孩子嗎?

我很好奇MySQL爲什麼沒有。即使當我使用FORCE INDEX執行計劃不會改變。

這是查詢至今,憑藉5是父節點的ID:

SELECT 
    @last_id := id AS id, 
    parent_id, 
    name, 
    @depth := IF(parent_id = 5, 1, @depth + 1) AS depth 
FROM 
    tree FORCE INDEX (index_parent_id, PRIMARY, index_both), 
    (SELECT @last_id := 5, @depth := -1) vars 
WHERE id = 5 OR parent_id = @last_id OR parent_id = 5 

Try live example at SQLfiddle

注意,之所以不能是小數據集,因爲當我指定FORCE INDEX (id)FORCE INDEX (parent_id)FORCE INDEX (id, parent_id)時,行爲不會改變...

該文檔說:

您也可以使用FORCE INDEX,其行爲像USE INDEX(index_list),但除了假定表掃描非常昂貴。換句話說,只有在無法使用某個給定索引來查找表中的行時才使用表掃描。

必須有一些呈現查詢無法使用INDEX,但我不明白它是什麼。


免責聲明:我知道有不同的方式來存儲和檢索SQL分層數據。我知道嵌套集模型。我沒有尋找替代實施。我不是在尋找嵌套集合。

我也知道查詢本身是堅果,併產生錯誤的結果。

我只是想,爲什麼MySQL是不是在這種情況下使用INDEX理解(詳細)。

+0

有時一個表有這麼幾條記錄,使用索引的開銷比讀取整個表的時間要多。 – Randy 2012-07-09 21:56:21

+0

@randy現在有一個似是而非的論點... – xandercoded 2012-07-09 21:57:04

+0

@Randy看到更新的問題 – Kaii 2012-07-09 22:03:14

回答

2

原因在於該WHERE子句在使用OR條件範圍內。

爲了說明這一點,嘗試運行查詢,這一次只用id = 5條件,並得到(EXPLAIN輸出):

+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+ 
| id | select_type | table  | type | possible_keys  | key  | key_len | ref | rows | Extra   | 
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+ 
| 1 | PRIMARY  | <derived2> | system | NULL    | NULL | NULL | NULL | 1 |    | 
| 1 | PRIMARY  | tree  | const | PRIMARY,index_both | PRIMARY | 4  | const | 1 |    | 
| 2 | DERIVED  | NULL  | NULL | NULL    | NULL | NULL | NULL | NULL | No tables used | 
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+ 

而且,這一次只用parent_id = @last_id OR parent_id = 5條件,並獲得:

+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+ 
| 1 | PRIMARY  | <derived2> | system | NULL   | NULL | NULL | NULL | 1 |    | 
| 1 | PRIMARY  | tree  | ALL | index_parent_id | NULL | NULL | NULL | 10 | Using where | 
| 2 | DERIVED  | NULL  | NULL | NULL   | NULL | NULL | NULL | NULL | No tables used | 
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+ 

MySQL在處理同一查詢中的多個索引時不太好。在AND條件下情況稍好些;與index union優化相比,更有可能看到index_merge優化。

隨着版本的進步,情況正在改善,但是我已經測試過您在版本5.5上的查詢,該版本位於當前最新的生產版本,結果如​​您所描述的那樣。

要解釋爲什麼這很困難,請考慮:兩個不同的索引將針對查詢的兩個不同條件作出回答。一個將回答id = 5,另一個爲parent_id = @last_id OR parent_id = 5(順便說一句,內沒有問題,因爲兩個條款都是從同一索引內處理的)。

沒有一個索引可以爲兩者都回答,因此FORCE INDEX指令被忽略。看,FORCE INDEX說MySQL必須在表掃描上使用索引。這並不意味着它必須在表掃描中使用多個索引。

所以MySQL遵循這裏的文檔規則。但爲什麼這麼複雜呢?因爲要使用這兩個索引來回答問題,MySQL必須從兩者收集結果,在管理第二個時將其存放在一些臨時緩衝區中。然後必須通過該緩衝區來過濾出相同的行(可能某行適合所有條件)。然後掃描該緩衝區以返回結果。

但是等等,那個緩衝本身本身沒有索引。過濾重複項不是一項明顯的任務。所以MySQL更喜歡在原始表上工作,並在那裏進行掃描,並避免所有這些混亂。

當然這是可以解決的。甲骨文公司的工程師可能會改進這一點(最近他們一直在努力改進查詢執行計劃),但我不知道這是否在TODO任務上,或者它是否具有高優先級。

+0

非常感謝你爲這個精心製作的答案! – Kaii 2012-07-10 16:27:21