2012-06-20 81 views
1

有一個非常大的表格,由8個字段組成(我知道,非常不準確)。優化SELECT查詢 - 索引使用等

在我的應用程序反覆運行這個SELECT:

SELECT d1, time, s1, s2 from Collection WHERE (d1 = 1) and (s1 = 1) and (time BETWEEN 5666300000 AND 566630700); 

我運行這個選擇具有不同的時間範圍。 d1是具有200-300個不同值的字段,與s1相同。主鍵是d1,時間,s1。

我正在尋找優化我的表結構和查詢的提示。事情是,時間字段不是升序/降序。所以這可能是一個需要一些時間的查詢。我想知道索引我的時間字段。我會不得不改變我的查詢?我那種情況,怎麼樣?

有沒有obv錯誤與我沒有看到我的查詢?這在我的應用程序中運行緩慢。 謝謝!

+0

請注意,在獨特(在您的情況下:主)鍵中使用_time_值被認爲是不好的做法。 – npe

+0

好的,thx。任何想法我可以做什麼改變?順便說一句,任何PRAGMA設置,可以使這重複查詢更快一般? –

回答

1

我建議您按照時間+ d1 + s1(按此順序)構建聚簇索引(主鍵)。這將確保數據按時間順序物理存儲,然後d1和s1

1

首先,正如npe所說的,您不應該將時間用作Primary。我認爲在time - d1 - s1上添加主要索引。通過這種方式,您將有時間作爲主要的主要索引,因此所有這些之間的速度將非常快。只有這樣纔會出現d1和s1。此外,將d1和s1放在儘可能小的數據類型中。如果它只有1和0,把它放到布爾等等。這將加速檢查。

+0

PRAGMA設置呢?增加cache_size到更大的東西? –

+0

你試過了嗎? –

+0

是的,試圖將它設置爲5000.沒有讓我有更好的表現。我認爲這可能會有幫助,但顯然沒有。看過其他選項,如同步=關閉,temp_store =內存和journal_mode =內存。但是我沒有注意到有任何提高的表現。 –

2

我不同意在其他答案中提出的主鍵的順序。

您的理想場景(對於您的確切示例查詢)是讓所有相關記錄彼此相鄰。這將啓用對您的數據的單一查詢。例如,使用(d1, s1, time)作爲聚集主鍵,你就必須存儲的數據如下...

d1 | s1 | time 
----+----+------ 
    1 | 1 | 1234 
    1 | 1 | 1235 \ 
    1 | 1 | 1236  SELECT * FROM table WHERE d1 = 1 AND s1 = 1 AND time BETWEEN 1235 AND 1237 
    1 | 1 | 1237 /
    1 | 1 | 1238 
    1 | 2 | 1234 
    1 | 2 | 1235 
    1 | 2 | 1236 
    1 | 2 | 1237 
    1 | 2 | 1238 

如果由其他人的建議,你有time作爲第一個字段在羣集索引,你做不是在一個連續的塊中獲取你的所有數據。相反,你得到一個尋求每個人的時間價值......

time | d1 | s1 
------+----+---- 
1234 | 1 | 1  *Desired Row 1 
1234 | 1 | 2 
1235 | 1 | 1  *Desired Row 2 
1235 | 1 | 2 
1236 | 1 | 1  *Desired Row 3 
1236 | 1 | 2 
1237 | 1 | 1  *Desired Row 4 
1237 | 1 | 2 
1238 | 1 | 1  *Desired Row 5 
1238 | 1 | 2 

這種結構實際上是一個不同的查詢非常好... ...

SELECT * FROM yourTable WHERE time = 1234 AND d1 = 1 AND s2 BETWEEN 2 AND 3 

這表明,三項方是沒有單一的普遍完美聚簇索引。那麼,你怎麼選擇做什麼聚集,因爲你只能有一個聚集索引?

這取決於您的數據和您的查詢。對於每個查詢,您需要查看將要撤出的多少個不同連續的數據塊。儘量減少這些塊的數量是一個非常好的主意。但維護數據的順序也是如此,以便它適合GROUP BY或ORDER by子句。 JOIN進一步加強了這一點。

對於您的示例查詢,我建議的第一個索引確實是最好的。但不是所有的疑問。

另外,您需要考慮分段。數據存儲在頁面中,您需要考慮數據插入的方式(在考慮更新時將其視爲刪除和插入)。因爲可能是任何插入通常會比現有數據更新的時間值,首先在聚集索引中有time會減少碎片。

例如,假裝每個頁面只能容納三行數據。上面建議的兩個索引看起來像這樣...

d1 | s1 | time   time | d1 | s1 
----+----+------   ------+----+---- 
    1 | 1 | 1234 \   1234 | 1 | 1 \ 
    1 | 1 | 1235 Page 1 1234 | 1 | 2 Page 1 
    1 | 1 | 1236/  1235 | 1 | 1 /
----+----+------   ------+----+---- 
    1 | 1 | 1237 \   1235 | 1 | 2 \ 
    1 | 1 | 1238 Page 2 1236 | 1 | 1 Page 2 
    1 | 2 | 1234/  1236 | 1 | 2 /
----+----+------   ------+----+---- 
    1 | 2 | 1235 \   1237 | 1 | 1 \ 
    1 | 2 | 1236 Page 3 1237 | 1 | 2 Page 3 
    1 | 2 | 1237/  1238 | 1 | 1 /
----+----+------   ------+----+---- 
    1 | 2 | 1238 -Page 4 1238 | 1 | 2 -Page 4 

現在,嘗試插入d1 = 1, s1 = 1, time = 1239

d1 | s1 | time   time | d1 | s1 
----+----+------   ------+----+---- 
    1 | 1 | 1234 \   1234 | 1 | 1 \ 
    1 | 1 | 1235 Page 1 1234 | 1 | 2 Page 1 
    1 | 1 | 1236/  1235 | 1 | 1 /
----+----+------   ------+----+---- 
    1 | 1 | 1237 \   1235 | 1 | 2 \ 
    1 | 1 | 1238 Page 2 1236 | 1 | 1 Page 2 
*1 | 1 | 1239*/   1236 | 1 | 2 /
----+----+------   ------+----+---- 
    1 | 2 | 1234 -Page 3 1237 | 1 | 1 \ 
----+----+------   1237 | 1 | 2 Page 3 
    1 | 2 | 1235 \   1238 | 1 | 1 /
    1 | 2 | 1236 Page 4 ------+----+---- 
    1 | 2 | 1237/  1238 | 1 | 2 -Page 4 
----+----+------   1239 | 1 | 1 /
    1 | 2 | 1238 -Page 5 

左邊的版本不得不創建一個新頁面。右側的版本繼續填充現有頁面。

出現碎片時,經常會有維修計劃可以糾正碎片。這通常是一個過夜的過程。

這一切都有點複雜,不是嗎?那麼,關於這個話題就有整本書。

我通常不會過分擔心碎片,直到它成爲問題。但它確實值得記住。

+0

什麼是體面的努力! :O –

+0

會通過說:添加索引:CREATE INDEX time_indx ON Collection(time);提高選擇查詢的性能?或者明確地在任何其他列上添加索引? –

+0

@ Ole-M - 「每列一列三個索引」與「三列一列索引」有趣的不同。無論您的建議是否有用,都取決於已有的索引,SQLite是否可以在生成計劃時合併索引以及其他一系列因素。 * [簡單的答案就是試試看。] *如果你真的想在'WHERE d1 =? AND s2 =?和時間BETWEEN? AND?',然後依次在'(d1,s2,time)'上創建一個索引。 *(這是一個很大的話題,對於一個SO問題來說太大了。)* – MatBailie