相信很多人在mysql中看到了where條件中使用到了or就會以為這樣是不會走索引的,通常會使用union all或者in 來進行優化,事實並不是想象的這樣具體問題具體分析。 下麵我們來看看 首先我們用sysbench生成兩個100w行的表 表結構如下 1.首先我們使用同一列帶索引欄位的進行查詢。 ...
相信很多人在mysql中看到了where條件中使用到了or就會以為這樣是不會走索引的,通常會使用union all或者in 來進行優化,事實並不是想象的這樣具體問題具體分析。
下麵我們來看看
首先我們用sysbench生成兩個100w行的表
表結構如下
mysql> show create table sbtest1 \G; *************************** 1. row *************************** Table: sbtest1 Create Table: CREATE TABLE `sbtest1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `k` int(11) NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `k_1` (`k`), KEY `c_1` (`c`) ) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1 1 row in set (0.00 sec) ERROR: No query specified mysql> show create table sbtest2 \G; *************************** 1. row *************************** Table: sbtest2 Create Table: CREATE TABLE `sbtest2` ( `id` int(11) NOT NULL AUTO_INCREMENT, `k` int(11) NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `k_2` (`k`), KEY `c_2` (`c`) ) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1 1 row in set (0.00 sec) ERROR: No query specified
1.首先我們使用同一列帶索引欄位的進行查詢。
mysql> explain select * from sbtest1 where k='501462' or k='502480'; +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | sbtest1 | NULL | range | k_1 | k_1 | 4 | NULL | 214 | 100.00 | Using index condition | +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
從執行計劃中看出這樣是可以使用到索引的,另外我們使用in 或者union all來看。
mysql> explain select pad from sbtest1 where k in ('501462','502480'); +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | sbtest1 | NULL | range | k_1 | k_1 | 4 | NULL | 214 | 100.00 | Using index condition | +----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
in的執行計劃和or相同。
mysql> explain select pad from sbtest1 where k='501462' union all select pad from sbtest1 where k='502480'; +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | PRIMARY | sbtest1 | NULL | ref | k_1 | k_1 | 4 | const | 113 | 100.00 | NULL | | 2 | UNION | sbtest1 | NULL | ref | k_1 | k_1 | 4 | const | 101 | 100.00 | NULL |
雖然執行計劃不通但union all估計的查詢行數和上面相同。
2.我們再來看看不同列帶索引欄位的進行查詢
mysql> explain select pad from sbtest1 where k='501462' or c='68487932199-96439406143-93774651418-41631865787-96406072701-20604855487-25459966574-28203206787-41238978918-19503783441'; +----+-------------+---------+------------+-------------+---------------+---------+---------+------+------+----------+-----------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------------+---------------+---------+---------+------+------+----------+-----------------------------------+ | 1 | SIMPLE | sbtest1 | NULL | index_merge | k_1,c_1 | k_1,c_1 | 4,120 | NULL | 114 | 100.00 | Using union(k_1,c_1); Using where | +----+-------------+---------+------------+-------------+---------------+---------+---------+------+------+----------+-----------------------------------
這樣的情況也會使用索引
如果or的條件中有個條件不帶索引的話,那這條sql就不會使用到索引了,如下。
mysql> explain select pad from sbtest1 where k='501462' or pad='00592560354-80393027097-78244247549-39135306455-88936868384'; +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | sbtest1 | NULL | ALL | k_1 | NULL | NULL | NULL | 986400 | 19.00 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
pad列沒索引所以整條的sql就不會使用到索引
假設使用union all來改寫一樣需要全表掃描所以意義也不大,如下
mysql> explain select pad from sbtest1 where k='501462' union all select pad from sbtest1 where pad='00592560354-80393027097-78244247549-39135306455-88936868384'; +----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+ | 1 | PRIMARY | sbtest1 | NULL | ref | k_1 | k_1 | 4 | const | 113 | 100.00 | NULL | | 2 | UNION | sbtest1 | NULL | ALL | NULL | NULL | NULL | NULL | 986400 | 10.00 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+
3.接下來我們看看多表關聯查詢
mysql> explain select a.pad,b.pad from sbtest1 a,sbtest2 b where a.id=b.id and (a.c='123' or b.c='1234'); +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+ | 1 | SIMPLE | a | NULL | ALL | PRIMARY,c_1 | NULL | NULL | NULL | 986400 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | eq_ref | PRIMARY,c_2 | PRIMARY | 4 | test.a.id | 1 | 100.00 | Using where | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec) mysql> explain select a.pad,b.pad from sbtest1 a,sbtest2 b where a.id=b.id and (a.c='123' or a.c='1234'); +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-----------------------+ | 1 | SIMPLE | a | NULL | range | PRIMARY,c_1 | c_1 | 120 | NULL | 2 | 100.00 | Using index condition | | 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.a.id | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-----------------------+ 2 rows in set, 1 warning (0.00 sec) mysql>
可以看出在多表查詢的情況下or條件如果不在同一個表內執行計劃表a的查詢不走索引。
我們試試看用union all來進行改寫
mysql> explain select a.pad,a.c from sbtest1 a,sbtest2 b where a.id=b.id and a.c='123' union all select a.pad,a.c from sbtest1 a,sbtest2 b where a.id=b.id and b.c='1234'; +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+ | 1 | PRIMARY | a | NULL | ref | PRIMARY,c_1 | c_1 | 120 | const | 1 | 100.00 | NULL | | 1 | PRIMARY | b | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.a.id | 1 | 100.00 | Using index | | 2 | UNION | b | NULL | ref | PRIMARY,c_2 | c_2 | 120 | const | 1 | 100.00 | Using index | | 2 | UNION | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.b.id | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
在or的條件不在同一個表的情況下 使用union all來改寫掃描行數減少且會走索引。