使用where子句中的OR优化sql语句

时间:2014-05-09 09:34:01

标签: mysql sql

在5Gb大小的消息表上运行此查询。 问题是执行需要> 3分钟。

SELECT m.id FROM messages m 
          LEFT JOIN dialog d on m.id=d.mid 
          WHERE (SELECT count(*) 
          FROM dialog 
          WHERE (m.from_id=uid1 and m.user_id=uid2) 
          OR (m.from_id=uid2 and m.user_id=uid1))=0 && read_state=0             
          LIMIT 100            

据我所知,通过NESTED SELECT IN WHERE CLAUSE进行搜索是一种不好的做法,但是却没有找到另一种方法来选择这样的行。试图将OR拆分为2个UNION语句,但它也很长。

消息表结构:

+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| from_id    | int(11)     | NO   | MUL | NULL    |                |
| user_id    | int(11)     | NO   | MUL | NULL    |                |
| group_id   | int(11)     | NO   |     | NULL    |                |
| to_number  | varchar(30) | NO   | MUL | NULL    |                |
| msg        | text        | NO   |     | NULL    |                |
| image      | varchar(20) | NO   |     | NULL    |                |
| date       | bigint(20)  | NO   |     | NULL    |                |
| read_state | tinyint(1)  | NO   |     | 0       |                |
| removed    | tinyint(1)  | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+

对话框表结构

+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(11)          | NO   | PRI | NULL    | auto_increment |
| uid1      | int(11)          | NO   | MUL | NULL    |                |
| uid2      | int(11)          | NO   | MUL | NULL    |                |
| mid       | int(11)          | NO   |     | NULL    |                |
| anonym_id | int(10) unsigned | NO   |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+

的MySQL>从消息中显示索引;

+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| messages |          0 | PRIMARY   |            1 | id          | A         |    12560908 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | to_number |            1 | to_number   | A         |      161037 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | from_id   |            1 | from_id     | A         |      157011 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | from_id   |            2 | to_number   | A         |      169742 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | user_id_2 |            1 | user_id     | A         |      314022 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | user_id_2 |            2 | read_state  | A         |      380633 |     NULL | NULL   |      | BTREE      |         |               |
| messages |          1 | user_id_2 |            3 | removed     | A         |      392528 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

的MySQL>从对话框中显示索引;

+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| dialog |          0 | PRIMARY  |            1 | id          | A         |     3125615 |     NULL | NULL   |      | BTREE      |         |               |
| dialog |          1 | uid1     |            1 | uid1        | A         |      520935 |     NULL | NULL   |      | BTREE      |         |               |
| dialog |          1 | uid1     |            2 | uid2        | A         |     3125615 |     NULL | NULL   |      | BTREE      |         |               |
| dialog |          1 | uid2     |            1 | uid2        | A         |     1562807 |     NULL | NULL   |      | BTREE      |         |               |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

EXPLAIN EXTENDED

+----+--------------------+--------+-------+---------------+------+---------+------+----------+----------+--------------------------+
| id | select_type        | table  | type  | possible_keys | key  | key_len | ref  | rows     | filtered | Extra                    |
+----+--------------------+--------+-------+---------------+------+---------+------+----------+----------+--------------------------+
|  1 | PRIMARY            | m      | ALL   | NULL          | NULL | NULL    | NULL | 22190398 |   100.00 | Using where              |
|  1 | PRIMARY            | d      | ALL   | NULL          | NULL | NULL    | NULL |  3125621 |   100.00 |                          |
|  2 | DEPENDENT SUBQUERY | dialog | index | uid1,uid2     | uid1 | 8       | NULL |  3125621 |   100.00 | Using where; Using index |
+----+--------------------+--------+-------+---------------+------+---------+------+----------+----------+--------------------------+

1 个答案:

答案 0 :(得分:4)

首先想到的是更改子查询查询以使用not exists而不是count(*)。第二种是将其拆分为两个独立的子查询。第三是添加索引:

create index idx_messages_read_state_4 on messages_read_state(user_id, from_id, user_id, id);
create index idx_dialog_2 on dialog(uid1, uid2)

第四个是删除外部查询中的left join对话框。没有使用dialog中的字段,而left join表示它未用于过滤。

然后查询:

select m.id
from messages m
where m.read_state = 0 and
      not exists (select 1
                  from dialog d
                  where m.from_id = d.uid1 and m.user_id = d.uid2
                 ) and
      not exists (select 1
                  from dialog d
                  where m.from_id = d.uid2 and m.user_id = d.uid1
                 )
limit 100;