MySQL Views OR vs IN子句

itz*_*nsn 5 mysql views

MySQL服务器版本:5.1.41在Ubuntu 10.04上

我在修改一些查询时想到了MySQL的行为,并且想知道它的原因.

基本上我正在创建一个视图.当我查询视图时,结果集是相同的但是,对于IN子句而言,读取的行数与子句不同OR.下面是一个简单的例子:

CREATE TABLE country ( 
    id_country int(11) NOT NULL AUTO_INCREMENT, 
    name varchar(50) NOT NULL, 
    PRIMARY KEY (id_country) 
) ENGINE=InnoDB; 

INSERT INTO country (name) VALUES ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ('H'); 

CREATE TABLE status ( 
    id_status int(11) NOT NULL AUTO_INCREMENT, 
    id_country int(11) NOT NULL, 
    status tinyint(4) NOT NULL, 
    PRIMARY KEY (id_status) 
) ENGINE=InnoDB; 
ALTER TABLE status ADD INDEX ( id_country ); 
ALTER TABLE status ADD FOREIGN KEY ( id_country ) REFERENCES test.country (id_country) ON DELETE RESTRICT ON UPDATE RESTRICT ; 

INSERT INTO status(id_country, status) VALUES 
(1,0), (2,1), (3,0), (4,1), (5,0),(6,1), (7,0), (8,1); 

CREATE ALGORITHM=MERGE VIEW view_country 
AS 
    SELECT c.*, s.id_status, s.status 
    FROM country c JOIN status s ON c.id_country = s.id_country; 
Run Code Online (Sandbox Code Playgroud)

下面的2个解释语句显示了解析的不同行数

mysql> EXPLAIN EXTENDED  SELECT * FROM view_country WHERE id_country IN (1, 2, 3)\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: c
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 3
     filtered: 100.00
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: s
         type: ref
possible_keys: id_country
          key: id_country
      key_len: 4
          ref: test.c.id_country
         rows: 1
     filtered: 100.00
        Extra: 
2 rows in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

使用OR子句

mysql> EXPLAIN EXTENDED SELECT * FROM view_country WHERE id_country = 1 OR id_country = 2 OR id_country = 3\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: s
         type: ALL
possible_keys: id_country
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 8
     filtered: 37.50
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: c
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: test.s.id_country
         rows: 1
     filtered: 100.00
        Extra: 
2 rows in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

如果你查看两个查询中的"行" - 它们的加法不同

query with OR子句读取的行数较少IN,这相当于巨大的表和连接.

有人能帮助我理解为什么会这样吗?

感谢您的时间.

Mar*_*ams 1

请注意,执行计划与索引的状态和表的大小有很大关系。即使对于相似的查询,MySQL 的执行也可能不同,有时 MySQL 甚至可能猜测错误。

具有 JOIN 的视图肯定会使事情变得复杂,因此您的 SELECT 语句并不那么简单。MySQL 为 IN 和 OR 选择不同的执行计划,请不要感到惊讶。

在第一个查询的情况下,MySQL 选择对两个查询都使用索引,这会在 EXPLAIN 中产生特定且准确的行计数。

然而,在第二个查询中,MySQL 选择扫描状态表中的所有行。这是有道理的,因为行数很少,而且 MySQL 无论如何都必须访问该表,因为没有覆盖索引可以返回所有需要的行。如果第二个查询实际上并不比第一个查询快,我不会感到惊讶。另外,请注意 EXPLAIN 中的行数(用于扫描)是估计值,因此在分析查询时请考虑到这一点。

第一个查询必须执行 6 次查找,而第二个查询在非常短的表扫描后只需执行 3 次查找。

MySQL 有很多技巧,有时仅限于非常特定的场景,以根据当前索引和行数来尝试优化查询。有记录的案例表明,对于类似的查询,MySQL 将采用两种不同的方法并最终得到相同的执行路径。在其他情况下,两个完全不同的执行计划会导致相似的性能,这就是其中一种情况。

无论如何,我希望这能向您解释为什么存在差异,但只要结果相同并且性能相似,就没有什么可担心的。

在某些情况下,正如我之前所说,MySQL 不会做出最佳猜测,然后您可以使用索引提示和自然连接等工具。就你的情况而言,我认为 MySQL 表现得很好。

要研究性能和执行计划,请查看以下两个站点: