2017-10-09 56 views
1

請考慮下面的例子:sqlite的選擇錯誤的查詢計劃

DROP TABLE IF EXISTS t1; 
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID; 
WITH RECURSIVE 
    cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000) 
INSERT INTO t1(a,b) SELECT x, x FROM cnt; 
CREATE INDEX t1b ON t1(b); 

此查詢創建表而不ROWID列並插入值(x,x),其中 X < 2000年爲了幫助查詢規劃允許運行分析。

ANALYZE; 

EXPLAIN QUERY PLAN 
SELECT * FROM t1 WHERE b BETWEEN 500 AND 2500; 

EXPLAIN QUERY PLAN 
SELECT * FROM t1 WHERE b BETWEEN 2900 AND 3000; 

在這兩種情況下的輸出是:0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1b (b>? AND b<?) 然而,沒有必要使用索引(第一個查詢),理由是反正我們通過整個表進行迭代,所以普通掃描表似乎效率更高。究竟這種方式與rowid的工作表:

DROP TABLE IF EXISTS t1; 
CREATE TABLE t1(a, b); 
WITH RECURSIVE 
    cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000) 
INSERT INTO t1(a,b) SELECT x, x FROM cnt; 
CREATE INDEX t1a ON t1(a); 
ANALYZE; 

EXPLAIN QUERY PLAN 
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500; 

EXPLAIN QUERY PLAN 
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000; 

在這種情況下,輸出將是:0|0|0|SCAN TABLE t1 0|0|0|SEARCH TABLE t1 USING INDEX t1a (a>? AND a<?)

所以,可能有人解釋查詢規劃如何優化查詢,而不ROWID表?

+1

我真的不明白爲什麼有些人喜歡DV這個漂亮的問題。他們甚至讀過或理解這個問題嗎?我upvoted抗衡;) –

回答

2

在這兩種情況下的輸出是: 0 | 0 | 0 |檢索表T1使用覆蓋索引T1B

然而,沒有必要使用指數((B>和B <?)對於 第一查詢)爲反正我們通過 整個表進行迭代的原因,所以普通掃描表似乎更有效。

你錯過了覆蓋索引部分:這意味着它正在使用僅索引 - 不訪問表的。

你是正確的,一個普通索引訪問(沒有「解蔽」)可能會慢於全表掃描,如果需要的所有行,但是這不是一個僅索引掃描的情況。

瞭解更多關於僅索引掃描這裏:http://use-the-index-luke.com/sql/clustering/index-only-scan-covering-index

編輯

WITHOUT ROWID在SQLite的什麼所謂的聚集索引在其他數據庫中:它們包含所有表列。因此,沒有必要訪問表,即使您選擇的所有列(如在select *)。

瞭解更多有關聚簇索引的位置:http://use-the-index-luke.com/sql/clustering/index-organized-clustered-index

+0

等待,但爲什麼在第二種情況下(當我們使用ROWID列的表)這是說,搜索使用普通的指數?我建議它使用索引t1a搜索rowid,然後使用rowid搜索'a'。所以字面上它不是覆蓋指數。我對嗎? – NikitaRock

+0

@NikitaRock對我的回答添加了評論。 –

+0

所以是沒有意義的運行,以優化查詢表上沒有的rowid分析? – NikitaRock