2009-10-24 62 views
3

當使用索引找不到結果時,有什麼辦法可以禁止MySQL執行全表掃描?禁止MySQL在查詢中使用全表掃描

例如下面的查詢:

SELECT * 
FROM a 
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1; 

僅僅是有效的,如果X滿足該條件,至少有1返回行,但如果條件不能由表中的任何數據來滿足,全盤掃描會被執行,這可能是非常昂貴的。

我不想優化這個特定的查詢,它只是一個例子。

EXPLAIN此查詢與X或外部的範圍:

id select_type table type possible_keys key key_len ref rows filtered Extra 
1 SIMPLE a range long_ip long_ip 8 \N 116183 100.00 Using where 

狀態變量顯示出更好的信息。對於範圍之外的X:

Handler_read_prev 84181 
Key_read_requests 11047 

在範圍:

Handler_read_key 1 
Key_read_requests 12 

如果只是有辦法阻止Handler_read_prev從日益增長過去1

更新。我不能接受我自己的答案,因爲它沒有真正回答這個問題(儘管如此,HANDLER是一個很棒的功能)。在我看來,沒有通用的方法來防止MySQL進行全面掃描。雖然像key ='X'這樣簡單的條件將被視爲「不可能的地方」,但像BETWEEN這樣的更復雜的東西不會。

+0

我假設你有一對索引(B,C),而不是每個單獨的列,對不對?也許索引統計是錯誤的,你是否試圖在桌面上運行'ANALYZE'? – 2009-10-24 17:20:10

+0

是的,但是你的意思是「索引統計是錯誤的」?我沒有試圖優化這個查詢,我只是問是否有辦法禁止MySQL執行全表掃描。 – 2009-10-24 18:19:05

+0

查詢優化器依賴於數據統計信息。它根據統計數據確定成本並選擇最佳方式。如果統計數據不存在,成本估算就會出錯,它不會使用最好的方式獲取數據。我只是說這只是因爲在新創建索引的大表上,即使查詢沒有返回結果,它也會使用索引。 – 2009-10-24 18:26:04

回答

0

這裏是我想出了在最後:

HANDLER a OPEN; 
HANDLER a READ BC <= (X); 
HANDLER a CLOSE; 

BC是關鍵的(B,C)的名字。如果我們用B降序排序表,然後將結果存在保證等於

SELECT * 
FROM a 
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1; 

現在,如果X不在表中的範圍,我們只需要檢查Ac是大於X,如果不是,X肯定超出了範圍,我們不需要再進一步觀察。

雖然這不是很優雅,但您必須在每次插入或更新時使用表格。

1

你可以編寫一個「完全覆蓋」的子查詢,它只使用索引中可用的數據。根據返回的主鍵,您可以查找主表中的行。

以下查詢被完全覆蓋由索引上(ID),(B,ID),和(C,ID):

select * 
from a 
where id in (
    select id 
    from a 
    where x <= C 
    and id in (
     select id 
     from a 
     where B <= X 
    ) 
) 
limit 1 

每個SELECT使用一個指數:對(B最內的索引,ID);中間的SELECT使用(C,id)上的索引,而外部的SELECT使用主鍵。

+1

LIMIT和IN不被支持,除了這個查詢會非常慢,我不明白我的查詢沒有被索引覆蓋。事實上,問題在於,如何在索引中找不到答案(首先應該這樣做)來阻止MySQL。 – 2009-10-24 10:12:38

+0

你是對的關於限制在一個子查詢中,將其移動到外部查詢。沒有找到結果時,我建議的查詢不會執行表掃描,因爲中間的選擇將是空的。它應該很快,你試過了嗎? – Andomar 2009-10-24 11:16:07

+0

是的,它在10 mb的桌子上運行超過一秒鐘。 2個內部查詢返回數千行。 – 2009-10-24 12:25:59