我试图理解为什么直接查询需要大约0.5秒才能运行,但使用相同查询的视图需要大约10秒才能运行。 MySql v5.6.27。
直接查询:
select
a,b,
(select count(*) from TableA i3 where i3.b = i.a) as e,
func1(a) as f, func2(a) as g
from TableA i
where i.b = -1 and i.a > 1500;
直接查询'解释'结果:
id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,i,range,PRIMARY,PRIMARY,4,\N,3629,Using where
2,DEPENDENT SUBQUERY,i3,ALL,\N,\N,\N,\N,7259,Using where
View的定义/查询是相同的,没有'where'子句...
select
a,b,
(select count(*) from TableA i3 where i3.b = i.a) as e,
func1(a) as f, func2(a) as g
from TableA i;
查询视图:
select * from ViewA t where t.b = -1 and t.a > 1500;
查询“解释”结果:
id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,<derived2>,ALL,\N,\N,\N,\N,7259,Using where
2,DERIVED,i,ALL,\N,\N,\N,\N,7259,\N
3,DEPENDENT SUBQUERY,i3,ALL,\N,\N,\N,\N,7259,Using where
为什么针对视图的查询最终执行3次全表扫描,而直接查询执行~1.5?
答案 0 :(得分:0)
简短的回答是:MySQL优化器不够聪明。
当processing a view时,MySQL可以合并视图或为其创建临时表:
对于MERGE,合并了引用视图和视图定义的语句的文本,以便视图定义的某些部分替换语句的相应部分。
对于TEMPTABLE,视图的结果将被检索到临时表中,然后用于执行该语句。
这适用于与derived tables and subqueries非常相似的方式。
您正在寻找的行为是 merge 。这是默认值,MySQL会尽可能尝试使用它。如果不可能(或者更确切地说:如果MySQL 认为它是不可能的话),MySQL必须评估完整视图,无论你是否只需要一行。这显然需要更多时间,并且在您的视图中会发生这种情况。
有一些阻止MySQL使用 merge 算法的东西:
如果视图包含以下任何构造,则不能使用MERGE:
聚合函数(SUM(),MIN(),MAX(),COUNT()等)
DISTINCT
GROUP BY
HAVING
LIMIT
UNION或UNION ALL
选择列表中的子查询
分配给用户变量
仅指文字值(在这种情况下,没有基础表)
如果MySQL合并与否,您可以测试它:尝试创建指定合并算法的视图:
create algorithm=merge view viewA as ...
如果MySQL认为它不能合并视图,则会收到警告
1警告:1354此处暂不能使用视图合并算法(假设未定义的算法)
在您的情况下,选择列表中的子查询阻止了合并。这不是因为它不可能做到。你已经知道可以合并它:只需重写它。
但MySQL优化器没有看到这种可能性。它不是特定于视图:如果您直接使用未合并的视图代码,它实际上不会合并它:explain select * from (select a, b, ... from TableA i) as ViewA where ...
。你必须在MySQL 5.7上进行测试,因为MySQL 5.6原则上不会在这种情况下合并(因为在查询中,它假设你想要在这里有一个temptable,即使对于非常简单的派生可以合并的表格。默认情况下,MySQL 5.7会尝试执行此操作,但它不适用于您的视图。
随着优化器得到改进,在某些情况下,即使在选择列表中存在子查询的情况下,优化器也会合并,因此该列表有一些例外。基于MySQL的MariaDB实际上在合并优化方面要好得多,并且会像你一样合并你的视图 - 所以即使作为一台机器也可以这样做。
总而言之:MySQL优化器目前还不够聪明。
遗憾的是,除了测试MySQL是否接受algorithm=merge
然后不使用MySQL无法合并的视图而是自己合并它们之外,你不能做太多的事情。