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,这相当于巨大的表和连接.
有人能帮助我理解为什么会这样吗?
感谢您的时间.
请注意,执行计划与索引的状态和表的大小有很大关系。即使对于相似的查询,MySQL 的执行也可能不同,有时 MySQL 甚至可能猜测错误。
具有 JOIN 的视图肯定会使事情变得复杂,因此您的 SELECT 语句并不那么简单。MySQL 为 IN 和 OR 选择不同的执行计划,请不要感到惊讶。
在第一个查询的情况下,MySQL 选择对两个查询都使用索引,这会在 EXPLAIN 中产生特定且准确的行计数。
然而,在第二个查询中,MySQL 选择扫描状态表中的所有行。这是有道理的,因为行数很少,而且 MySQL 无论如何都必须访问该表,因为没有覆盖索引可以返回所有需要的行。如果第二个查询实际上并不比第一个查询快,我不会感到惊讶。另外,请注意 EXPLAIN 中的行数(用于扫描)是估计值,因此在分析查询时请考虑到这一点。
第一个查询必须执行 6 次查找,而第二个查询在非常短的表扫描后只需执行 3 次查找。
MySQL 有很多技巧,有时仅限于非常特定的场景,以根据当前索引和行数来尝试优化查询。有记录的案例表明,对于类似的查询,MySQL 将采用两种不同的方法并最终得到相同的执行路径。在其他情况下,两个完全不同的执行计划会导致相似的性能,这就是其中一种情况。
无论如何,我希望这能向您解释为什么存在差异,但只要结果相同并且性能相似,就没有什么可担心的。
在某些情况下,正如我之前所说,MySQL 不会做出最佳猜测,然后您可以使用索引提示和自然连接等工具。就你的情况而言,我认为 MySQL 表现得很好。
要研究性能和执行计划,请查看以下两个站点: