MySQL 视图 OR vs IN 子句
Posted
技术标签:
【中文标题】MySQL 视图 OR vs IN 子句【英文标题】:MySQL Views OR vs IN clause 【发布时间】:2012-02-22 18:31:34 【问题描述】: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)
如果您查看两个查询中的“行” - 它们加起来不同
与IN
相比,带有OR
子句的查询读取的行数更少,这增加了巨大的表和连接。
谁能帮我理解为什么会这样?
感谢您的宝贵时间。
【问题讨论】:
始终相同但结果不同? @Marcus - 对不起,我不明白这个问题 - 如果你的意思是我每次都得到一致的结果集并且每次解析的行数也是一致的 - 那么答案是肯定的 @Marcus Adams - 我有一个复制粘贴错误 - 我已经纠正了。问题不在于不同的结果集 - 结果集是相同的 - 但 IN 与 OR 之间读取的行数不同 - 如果您无法重现它,请告诉我 - 我在服务器版本上:5.1跨度> @MarcusAdams - 我想我很困惑,因为你写的这个评论清楚地表明它不是来自简单的 SELECT,而不是来自你显示的查询 - 你看到我是从一个视图查询吗?你看到我的视图定义了吗?为了确保我是理智的,我创建了一个空数据库-运行了我提到的所有查询,然后运行了解释语句-没有额外的查询或选择语句-您是否尝试使用我给出的 sql 创建表? - 如果你这样做了,但你仍然觉得缺少一些东西 - 让我知道对不起,我没有意识到您正在查询视图。我在下面提供了我的答案。 【参考方案1】:
请注意,执行计划与索引状态和表大小有很大关系。即使对于类似的查询,MySQL 也可能执行不同的操作,有时 MySQL 甚至会猜错。
带有 JOIN 的视图肯定会使事情复杂化,因此您的 SELECT 语句并不是那么简单。不要对 MySQL 为 IN 和 OR 选择不同的执行计划感到惊讶。
在第一个查询的情况下,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 表现得很好。
要研究性能和执行计划,请查看以下两个网站:
http://www.mysqlperformanceblog.com/ http://explainextended.com/【讨论】:
感谢 Marcus 的解释——作为一个好习惯,我认为我需要通过任何复杂的连接查询,看看 mysql 如何选择索引【参考方案2】:如果我理解正确,您得到的结果是相同的,并且您想知道“OR”和“IN”子句在速度方面的区别以及它们的工作方式。
如果是这样,我认为您的问题可能与此问题重复:IN vs OR in the SQL WHERE Clause
【讨论】:
我相信 OP 表明使用“IN”而不是“OR”的结果不同 啊,好吧,那我没听懂他的问题。我原以为结果会是一样的。 @ZackMacomber ,来自问题:“结果集相同”。 我提供了有关该问题的更多信息,包括服务器版本以及 EXPLAIN EXTENDED 的输出是什么。服务器版本可能很重要,因为有人说他无法在 mysql 5.6 上复制它 - 结果集是相同的,但为产生结果而读取的行数不同 - 这就是我感兴趣的 - @ypercube - 我在这里没有看到正确的东西吗?我刚刚在此页面上找到了 Google Chrome 中的“结果集相同”,我只返回了 1 个在您的评论中找到的结果。以上是关于MySQL 视图 OR vs IN 子句的主要内容,如果未能解决你的问题,请参考以下文章
带有联合错误的 MySQL 视图 - “视图的 SELECT 包含 FROM 子句中的子查询”
从带有 HAVING 子句的 MySQL 视图中选择返回空结果集
如何在 SQL Server 的 IN 子句中使用存储过程输出