我试图理解为什么直接查询需要大约 0.5 秒才能运行,而使用相同查询的 View 需要大约 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;
查询 View :
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
为什么针对 View 的查询最终执行了 3 次全表扫描,而直接查询执行了大约 1.5 次?
最佳答案
简短的回答是:MySQL 优化器不够聪明,无法做到这一点。
当 processing a view , MySQL 可以合并 View 或为其创建一个临时表:
For MERGE, the text of a statement that refers to the view and the view definition are merged such that parts of the view definition replace corresponding parts of the statement.
For TEMPTABLE, the results from the view are retrieved into a temporary table, which then is used to execute the statement.
这与 derived tables and subqueries 的应用方式非常相似也是。
您正在寻找的行为是合并。这是默认值,MySQL 将尽可能使用它。如果不可能(或者更确切地说:如果 MySQL 认为 这是不可能的),MySQL 必须评估完整的 View ,无论您是否只需要其中的一行。这显然需要更多时间,而且在您看来就是这样。
有一个列表阻止 MySQL 使用 merge 算法:
MERGE cannot be used if the view contains any of the following constructs:
Aggregate functions (SUM(), MIN(), MAX(), COUNT(), and so forth)
DISTINCT
GROUP BY
HAVING
LIMIT
UNION or UNION ALL
Subquery in the select list
Assignment to user variables
Refers only to literal values (in this case, there is no underlying table)
您可以测试 MySQL 是否会合并:尝试创建指定合并算法的 View :
create algorithm=merge view viewA as ...
如果 MySQL 认为它不能合并 View ,你会收到警告
1 warning(s): 1354 View merge algorithm can't be used here for now (assumed undefined algorithm)
在您的例子中,选择列表中的子查询 阻止了合并。这并不是因为不可能做到。您已经证明可以合并它:只需重写它即可。
但是 MySQL 优化器没有看到这种可能性。它不特定于 View :如果您直接使用未合并的 View 代码,它实际上也不会合并它: explain select * from (select a, b, ... from TableA i) as ViewA where ...
。你必须在 MySQL 5.7 上测试这个,因为 MySQL 5.6 原则上不会在这种情况下合并(因为,在查询中,它假设你想要在这里有一个 temptable,即使是非常简单的派生可以合并的表)。 MySQL 5.7 将默认尝试执行此操作,尽管它不适用于您的 View 。
随着优化器的改进,在某些情况下,即使在选择列表中有子查询的情况下,优化器也会合并,因此该列表有一些异常(exception)。 MariaDB ,它基于 MySQL,实际上在合并优化方面要好得多,并且会像您所做的那样合并您的 View - 因此即使作为一台机器也可以做到这一点。
总结一下:MySQL 优化器目前还不够聪明,无法做到这一点。
不幸的是,您对此无能为力,除了测试 MySQL 是否接受 algorithm=merge
然后不使用 MySQL 无法合并的 View ,而是自己合并它们。
关于MySql 性能查询与具有 'explain' 输出的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45621890/