当我们把几个表的某些字段抽取出来,然后用union all创建出一个view时,我们总希望这些表的索引能够在针对视图的查询时有效。很久以前,我凭着对sql server的SQL自动优化的超级相信,想当然地以为它能够做到这点。但是sql2000让我失望并且痛苦了一回。
假设我们有这样的两个表
X(a,b) Y(a)
在X上有分别针对a和b建立的索引IX_X_a,和IX_X_b,在Y上有索引IX_Y_a
接下来我们构造一个这样的 view(V)
select a,b from X
union all
select a,a from Y
然后我们对这样的两个查询进行分析:
1、select * from V where a = @s
2、select * from V where b = @s
看看执行计划,发现什么了吗? 对,在查询2中很悲惨地对Y表进行了全表扫描! 当Y表的数据越来越大,这个查询的速度就会越来越慢,缺少SEEK的下场可想而知。而据说,这个问题在sql2005中得到了解决,我们只能对现在仍不敢使用的2005再多一份期盼了。
那么在2000中我们无法去解决这个问题呢,放弃使用view?不行,这个view被大量多次地使用。使用分区视图?也不行,分区视图指的是这些用到的字段必须是相同。其实分区视图和2005的分区表是有些类似的。而此场景下的X,Y表完全是两个不同的东西(实际上X还有很多列,Y也还有很多列)。
我的解决方法就是对Y表增加一个b列,让它的值等于a,首当其冲的就是使用计算列了,formula为[a],再对视图查询看看,OK了,两个索引都会被利用到了。但是问题还是有的,在接下来的合并复制(Merge Replication)中,我遇到了意料不到的错误(此是后话,按下不表)!!! 所以我现在使用了trigger来做这件事,在insert时update b = a。
除了复制,这个视图仍然不够聪明,我实际上的视图大概是这样的:
select 'X' as type,a,b from X
union all
select 'Y' as type,a,b from Y
当我使用查询如下时
select * from V where type = 'X' and a = @s
SQL2000并不会聪明地只去对X表查询,它仍然对X,Y两个表都进行了索引上的SEEK操作,要知道,实际使用的这个V我union了8个表啊,这样操作时间就是8*T! 有人说了,在这个V上建立索引吧,这样可以时间减少到3+T了,事实上我尝试了这个操作,但后来我放弃了,VIEW上能够创建索引的前提条件太多了,我无法满足它,所以最终,我依然在8*T的日子中继续我的生活....
评论