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;
下面的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)
使用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)
如果你查看两个查询中的“行” - 它们的加法不同
OR
子句的查询与IN
相比读取的行数更少,这会增加巨大的表和连接数。
有人可以帮我理解为什么会这样吗?
感谢您的时间。
答案 0 :(得分:1)
请注意,执行计划与索引的状态和表的大小有很大关系。即使对于类似的查询,MySQL也可能以不同的方式执行,有时MySQL甚至会猜错。
使用JOIN的视图肯定会使事情变得复杂,所以SELECT语句并不那么简单。不要惊讶MySQL为IN选择不同的执行计划。
在第一个查询的情况下,MySQL选择为两个查询使用索引,这会导致EXPLAIN中的特定和准确的行数。
但是,在第二个查询中,MySQL已选择扫描状态表中的所有行。这是有道理的,因为行数太少而MySQL无论如何都要访问该表,因为没有覆盖索引会返回所有需要的行。如果第二个查询实际上不比第一个查询快,我不会感到惊讶。另请注意,EXPLAIN中的行数(用于扫描)是估算值,因此在分析查询时请考虑到这一点。
第一个查询必须进行6次查找,而第二次查询只需要在非常短的表扫描后进行3次查找。
MySQL有许多技巧,有时仅限于非常特定的场景,尝试根据当前索引和行数优化查询。有记录的案例,for similar queries, MySQL will take two different approaches and end up with the same execution path。在其他情况下,两个完全不同的执行计划会产生类似的性能,这就是其中一种情况。
无论如何,我希望这能解释为什么存在差异,但只要结果相同,且性能相似,就没有什么值得关注的了。
在某些情况下,正如我之前所说,MySQL不会做出最佳猜测,然后您可以使用索引提示和自然连接等工具。在你的情况下,我认为MySQL表现得很好。
要研究绩效和执行计划,请查看以下两个站点:
答案 1 :(得分:0)
如果我理解得当,你得到的结果是一样的,并且你想知道关于速度及其工作方式的'OR'和'IN'条款之间的区别。
如果是这样,我认为您的问题可能与此问题重复:IN vs OR in the SQL WHERE Clause