MySQL – 视图的实现
本文参考了高性能MySQL
数据库中的视图是一个虚拟表。同真实的表一样,视图包含一系列带有名称的行和列数据。行和列数据来自由定义视图查询所引用的表,并且在引用视图时动态生成。
视图的实现
视图中不存放任何数据,在使用SQL语句访问视图的时候,返回的数据时MySQL从其他表中生成的。视图和表在同一个命名空间,在很多地方对于视图和表是同样对待的。不过也有不同,不能对视图创建触发器,不能使用DROP TABLE命令删除视图。
假设我们创建一个视图:
create view Oceania as
select * from Country where Continent ='Oceania'
with check option;
实现视图最简单的方法是将select语句的结果存放到临时表中。当需要访问视图的时候,直接访问临时表就可以。比如:
select Code, Name from Oceania where Name =‘Australia’;
在实现上述查询时,有两种方式去实现,一种是使用临时表,还有一种是重写含有视图的查询,将视图的定义SQL直接包含进查询的SQL中:
使用临时表算法:
create temporary table TMP_Oceania_123 as
select * from Country where Continent = ‘Oceania’;
select Code, Name from TMP_Oceania_123where Name = ‘Australia’;
使用合并算法的:
select Code, Name from Country
where Continent = ‘Oceania’ and Name = ‘Australia’;
如果可能,会尽可能使用合并算法。下图是这两种算法实现的细节。
如果视图中包含Group by, Distinct, 任何聚合函数,union,子查询等,只要无法在原表记录和视图记录中建立一一映射的场景中,MySQL都会使用临时表算法来实现。可以使用Explain来确定MySQL使用的具体算法。
更新视图
视图可以被更新。只要指定了合适的条件,就可以更新,删除甚至向视图中写入数据。如果视图定义中包含了group by, union, 聚合函数以及其他一些特殊情况,就不能被更新了。更新视图的查询可以是一个关联语句,但是有一个限制,被更新的列必须来自同一个表中。所有使用临时表算法实现的视图都无法被更新。
视图的性能
使用临时表算法实现的视图,在某些时候性能会很糟糕(可能比直接使用等效查询语句要好一点)。MySQL会以递归的方式执行这些视图,先会执行外层查询,即使外层查询优化器将其优化的很好,但是,内外结合的优化却无法做到非常好。如果打算使用视图来提升性能,需要做比较详细的测试。即便是合并算法实现的视图也会有额外的开销,而且使徒的性能很难预测。
视图的限制
视图有很多限制,比如,MySQL不支持物化视图(将视图结果数据存放在一个可以查看的表中,并定期充原始表中刷新数据到这个表中)。也不支持在视图中创建索引。当然,可以通过构建缓存表或者汇总表的办法来模拟。也可以直接使用工具Flexviews来实现这个目的。
MySQL并不会保存视图定义的原始SQL语句,所以,如果我们希望简单的修改视图的结果来重新定义,有的人可能会直接使用show create view语句来查看之前的定义,但是该语句查询出来的视图创建语句会让人大失所望,没有格式化,没有注释,没有缩进,基本不可读。