2009-12-04 79 views
1

我想在其中有200k條記錄的表上使用以下查詢。有各種其他可以被過濾的字段,但這是一個基本的例子。MySQL索引策略

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted = '' AND 
b.publication_date <= '2009-12-04' AND 
(
    b.subject1_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR 
    b.subject2_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR 
    b.subject3_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') 
) 

目前,我對所有這些字段都有一個單獨的索引,這個查詢需要4.5秒,這太長了。 EXPLAIN列出密鑰下的NULL

我還嘗試創建一個包含上述查詢中所有字段的大型索引,但EXPLAIN顯示未使用此多字段索引。

如何索引這些字段以加快查詢速度?

編輯:這是我目前的指標(其中沒有一個似乎是由查詢使用):

  • 指數(價格)
  • 指數(刪除)
  • 指數(publication_date)
  • 指數(subject1_id)
  • 指數(subject2_id)
  • 指數(subject3_id)
  • 指數(價格,刪除,publication_date,subject1_id,subject2_id,subject3_id)

EDIT2:每ʞɔıu的答案 - 正火表和使用基本上是他查詢後,它加速它的一些(現在是時候〜3.5秒),但沒有我期待的那麼多。我將新表作爲PRIMARY KEY(isbn,subject_id)編制索引,並且此索引正在用於連接。

EDIT3:我在第二個表(subject_id,isbn)上添加了一個額外的索引,這有所幫助。在下面提到的其他索引的增加會有所幫助,但只有在查詢中使用「FORCE INDEX」時纔會使用。現在下降到大約1.5秒。是否有希望將其降低得多?

+0

嘗試對主題表 – 2009-12-04 21:57:35

+0

也給出了相同的連接上添加一個索引上添加(主題,ISBN)其他指數(ISBN,出版日期,已刪除,價格) – 2009-12-04 22:01:16

回答

3

在這種情況下,您需要對索引編制進行規範化,然後索引纔會有幫助。

您可以創建包含另一個表(主題,ISBN),書本和主題添加索引,然後再加入到該表,如:

select b.isbn from books b 
inner join book_subject bs on bs.isbn=b.isbn 
where 
    b.price is not null and b.deleted != 'DELETED' 
    AND b.publication_date <= '2009-12-04' 
    AND bs.subject in ('CAT1', 'CAT2'...) 

的模式規範化規則#1(字面意思)是: 「no repeating groups」。 在3個主題列的where子句中執行OR操作將阻止您能夠利用該部分查詢的索引。

(更新,以反映國際標準書號是主鍵)

+0

那麼,它沒有正常化的原因只是因爲那是源數據的格式。將不得不加入一個200k記錄的表格和另一個高達~60萬記錄的表格確實有助於查詢的效率? – Wickethewok 2009-12-04 21:09:33

+0

很有可能,因爲它可以使用索引,成本將是檢查行數* log *的函數。 – 2009-12-04 21:14:05

+0

相當不錯的東西 - 您可能希望將您對我的問題的評論添加到您的答案中,因爲主題表上的附加索引非常重要。 – Wickethewok 2009-12-04 22:37:38

0

我想更多地瞭解您的查詢的實際含義和可能幫助帶路到你的答案。

讓我們解開它來向你展示問題。

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted != 'DELETED' AND 
b.publication_date <= '2009-12-04' AND 
(
    b.subject1_id = 'CAT1' OR 
    b.subject1_id = 'CAT2' OR 
    b.subject1_id = 'CAT3' OR 
    b.subject1_id = 'CAT4' OR 
    b.subject1_id = 'CAT5' OR 
    b.subject2_id = 'CAT1' OR 
    b.subject2_id = 'CAT2' OR 
    b.subject2_id = 'CAT3' OR 
    b.subject2_id = 'CAT4' OR 
    b.subject2_id = 'CAT5' OR 
    b.subject3_id = 'CAT1' OR 
    b.subject3_id = 'CAT2' OR 
    b.subject3_id = 'CAT3' OR 
    b.subject3_id = 'CAT4' OR 
    b.subject3_id = 'CAT5' 

) 

很顯然,沒有任何索引會用於其他主題(price,deleted,publication_date,subject1)之外的其他索引。

索引中有哪些字段?

+0

關於索引字段,請參閱上面的編輯。 – Wickethewok 2009-12-04 21:15:57

0

關於尼克的帖子:

創建包含 (主題,book_id)另一個表上 書和主題添加索引:

豈不是更清潔有

select b.isbn from books b 

where 
#various table b where restrictions 

AND b.isbn IN (
    Select isbn 
    from book_subject bs 
    where bs.subject IN ('CAT1', 'CAT2' ...) 
) 
+0

我對你的回答有點困惑。如果有幫助,「isbn」是主鍵。 – Wickethewok 2009-12-04 21:12:27

+0

該查詢需要一個臨時表來存儲子查詢的中間結果;內部聯接方法不會。與其他RDBMS相比,mysql的子查詢操作並沒有得到很好的優化。 – 2009-12-04 21:19:22

+0

更新爲使用isbn作爲PK – Zak 2009-12-04 21:31:50

0

首先:MySQL在選擇期間每個表只能使用一個索引。它試圖選擇儘可能好的索引,但有時服務器有幾個原因無法做出決定。只有一個字段有多個索引只會幫助你,如果你有很多語句一次只運行一個where子句。

爲了優化在這裏:你需要建立一個不包括該領域

price 
deleted 
publication_date 

不包括類別,索引,因爲你正在使用的OR子句。

ALTER TABLE `test`.`books` ADD INDEX `idxPriceDeletedPublication`(`price`, `deleted`, `publication_date`); 

這應該然後給你以下EXPLAIN輸出:

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: b 
     type: range 
possible_keys: idxPriceDeletedPublication 
      key: idxPriceDeletedPublication 
     key_len: 5 
      ref: NULL 
     rows: 1 
     Extra: Using where 
1 row in set (0.00 sec) 
+0

它將無法爲已刪除列創建索引;你不能索引不等於。更好的策略是查詢deleted ='NOTDELETED'或任何與'DELETED'狀態相反的地方是 – 2009-12-04 21:40:01

+0

我添加了這三個組件索引。默認情況下,MySQL不會在我的查詢中使用它,當我使用「FORCE INDEX」時,出於某種原因實際需要13秒。 'DELETED'的反義詞是'',ʞɔıu是對的,我應該使用它。 – Wickethewok 2009-12-04 21:47:01