【问题标题】:Optimize sql statement with OR in where clause在 where 子句中使用 OR 优化 sql 语句
【发布时间】:2014-05-09 09:34:01
【问题描述】:

在 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      |         |               |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

解释扩展

+----+--------------------+--------+-------+---------------+------+---------+------+----------+----------+--------------------------+
| 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 |
+----+--------------------+--------+-------+---------------+------+---------+------+----------+----------+--------------------------+

【问题讨论】:

  • 你有dialog (mid)的索引吗?
  • (SELECT COUNT(*)...)=0 可以用 NOT EXISTS 代替,这样可读性更高,也可能效率更高。为什么你没有关于对话框(uid1,uid2)和对话框(uid2,uid1)的索引?这应该有助于子选择的执行。
  • 每张表有多少行?

标签: mysql sql


【解决方案1】:

首先想到的是将子查询更改为使用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 to dialog。没有来自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;

【讨论】:

  • 我认为 WHERE 中带有和 OR 子句的单个 NOT EXISTS 比两个单独的 NOT EXISTS 更有效。但是完全删除 JOIN 是很好的观察。我注意到 LEFT JOIN 可能没用......但你已经超越了一步。不错
  • 华丽流畅!非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-25
  • 2019-06-26
  • 1970-01-01
  • 1970-01-01
  • 2013-10-16
相关资源
最近更新 更多