如何通过简单的AND + OR查询使mysql使用索引?

时间:2012-07-12 01:35:18

标签: mysql sql indexing mysql5

这是使用MySQL 5.5。 我似乎无法说服MySQL为这些查询使用索引,并且它们需要2-10秒才能在一个包含110万行的表上运行。

表:

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列。在这两种情况下都没有改进或使用指数。

看起来使用索引限制在这里检查的行应该非常容易,所以我希望我只是遗漏了一些东西。

1 个答案:

答案 0 :(得分: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解释您实际上要执行的操作时,它会很有用。如果可以的话,将复杂问题简化为更简单的问题当然可以,但是要说'当你实际进行更新时,重新做一个SELECT,嗯,过度简化了。)