2012-07-12 24 views
0

這是使用MySQL 5.5。 我似乎無法說服MySQL使用這些查詢的索引,並且它們需要2-10秒的時間才能在有110萬行的表上運行。如何讓mysql使用帶有簡單AND + OR查詢的索引?

表:

CREATE TABLE `notifiable_events` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`key` varchar(255) NOT NULL, 
`trigger_profile_id` int(10) unsigned DEFAULT NULL, 
`model1` varchar(25) NOT NULL, 
`model1_id` varchar(36) NOT NULL, 
`model2` varchar(25) NOT NULL DEFAULT '', 
`model2_id` varchar(36) NOT NULL DEFAULT '', 
`event_data` text, 
`created` datetime DEFAULT NULL, 
`modified` datetime DEFAULT NULL, 
`deleted` tinyint(1) NOT NULL DEFAULT '0', 
PRIMARY KEY (`id`), 
KEY `key` (`key`), 
KEY `notifiable_events__trigger_profile` (`trigger_profile_id`), 
KEY `deleted` (`deleted`), 
KEY `noti_evnts__m2` (`model2`), 
KEY `noti_evnts__m1` (`model1`), 
CONSTRAINT `notifiable_events__trigger_profile` FOREIGN KEY (`trigger_profile_id`) REFERENCES `profiles` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1177918 DEFAULT CHARSET=utf8 

QUERY:

SELECT * 
    FROM notifiable_events 
    WHERE (`model1` = 'page' AND `model1_id` = '54321') 
     OR (`model2` = 'page' AND `model2_id` = '12345'); 

EXPLAIN(S):

 
mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model1` = 'page' AND `model1_id` = '922645') OR (`model2` = 'page' AND `model2_id` = '922645')\G 

    *************************** 1. row *************************** 
       id: 1 
     select_type: SIMPLE 
      table: notifiable_events 
      type: ALL 
    possible_keys: noti_evnts__m2,noti_evnts__m1,noti_evnts__m1_m2 
       key: NULL 
      key_len: NULL 
       ref: NULL 
      rows: 1033088 
     filtered: 100.00 
      Extra: Using where 
    1 row in set, 1 warning (0.00 sec) 

mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model1` = 'page' AND `model1_id` = '922645') OR (`model1` = 'page' AND `model1_id` = '922645')\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: notifiable_events 
     type: ref 
possible_keys: noti_evnts__m1,noti_evnts__m1_m2 
      key: noti_evnts__m1 
     key_len: 77 
      ref: const 
     rows: 1 
    filtered: 100.00 
     Extra: Using where 
1 row in set, 1 warning (0.00 sec) 

mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model2` = 'page' AND `model2_id` = '922645') OR (`model2` = 'page' AND `model2_id` = '922645')\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: notifiable_events 
     type: ref 
possible_keys: noti_evnts__m2 
      key: noti_evnts__m2 
     key_len: 77 
      ref: const 
     rows: 428920 
    filtered: 100.00 
     Extra: Using where 
1 row in set, 1 warning (0.00 sec) 

你可以看到,如果我只用MODEL1或只使用MODEL2那麼它會使用索引,但是,只要我嘗試將它們一起使用,就會完全放棄索引,並執行全表掃描。 我已經嘗試了FORCE INDEX,並且我嘗試了多鍵索引的組合(我爲此表留下了一個作爲示例)。我也嘗試重新排列查詢中元素的順序,但似乎也沒有任何效果。

更新: 我忘了提及我已經嘗試了ANALYZE和OPTIMIZE(每次多次,沒有改變)。我也已經在* _id(基數非常糟糕,這些列大多是唯一條目)和索引中使用了所有4列的索引。在這兩種情況下都沒有改進或使用指數。

它似乎應該很容易使用索引來限制在這裏檢查的行,所以我希望我只是錯過了一些東西。

+0

如果似乎'x_id'列應該被索引(改爲)?無論如何,如果表格統計信息被更新呢? – 2012-07-12 01:50:22

回答

4

or子句有時會導致查詢優化。

你可以嘗試一個工會,像這樣。它可能會重新激活索引。

SELECT * 
    FROM notifiable_events 
WHERE `model1` = 'page' AND `model1_id` = '54321' 
UNION 
SELECT * 
    FROM notifiable_events 
WHERE `model2` = 'page' AND `model2_id` = '12345' 

編輯,來回答這個問題的評論

如果你想更新使用這種選擇方案的記錄,你可以試試這個:

UPDATE notifiable_events 
    SET what=ever, 
     what=ever, 
     what=else 
WHERE id IN (
    SELECT id 
    FROM notifiable_events 
    WHERE `model1` = 'page' AND `model1_id` = '54321' 
    UNION 
    SELECT id 
    FROM notifiable_events 
    WHERE `model2` = 'page' AND `model2_id` = '12345' 
) 

(請注意,當您使用Stackoverflow來解釋您實際嘗試做什麼時很有幫助。當然可以將複雜問題簡化爲簡單的o如果可以的話,但是如果你正在做一個SELECT,當你真的在做一個UPDATE時,嗯,這是一個過於簡單化。)

+0

這需要一些DISTINCT-ish才能使結果完全相同。(或者可以多次選擇相同的記錄) – 2012-07-12 01:53:27

+2

文檔說明:UNION的默認行爲是將重複的行從結果中刪除。可選的DISTINCT關鍵字除缺省值外沒有其他作用,因爲它還指定刪除重複行。使用可選的ALL關鍵字,重複行刪除不會發生,並且結果包括所有SELECT語句中的所有匹配行。「 – 2012-07-12 01:56:20

+1

@pst」union'排序並刪除重複項。「union all」保留所有行保持不變編碼) – Bohemian 2012-07-12 01:58:25