技术开发 频道

十步优化SQL Server中的数据访问

  经过索引优化,重构TSQL后你的数据库还存在性能问题吗?完全有可能,这时必须得找另外的方法才行。SQL Server在索引方面还提供了某些高级特性,可能你还从未使用过,利用高级索引会显著地改善系统性能,本文将从高级索引技术谈起,另外还将介绍反范式化技术。

  第六步:应用高级索引

  实施计算列并在这些列上创建索引

  你可能曾经写过从数据库查询一个结果集的应用程序代码,对结果集中每一行进行计算生成最终显示输出的信息。例如,你可能有一个查询从数据库检索订单信息,在应用程序代码中你可能已经通过对产品和销售量执行算术操作计算出了总的订单价格,但为什么你不在数据库中执行这些操作呢?

  请看下面这张图,你可以通过指定一个公式将一个数据库表列作为计算列,你的TSQL在查询清单中包括这个计算列,SQL引擎将会应用这个公式计算出这一列的值,在执行查询时,数据库引擎将会计算订单总价,并为计算列返回结果。

  图 1 计算列

  使用计算列你可以将计算工作全部交给后端执行,但如果表的行数太多可能计算性能也不高,如果计算列出现在Select查询的where子句中情况会更糟,在这种情况下,为了匹配where子句指定的值,数据库引擎不得不计算表中所有行中计算列的值,这是一个低效的过程,因为它总是需要全表扫描或全聚集索引扫描。

  因此问题就来了,如何提高计算列的性能呢?解决办法是在计算列上创建索引,当计算列上有索引后,SQL Server会提前计算结果,然后在结果之上构建索引。此外,当对应列(计算列依赖的列)的值更新时,计算列上的索引值也会更新。因此,在执行查询时,数据库引擎不会为结果集中的每一行都执行一次计算公式,相反,通过索引可直接获得计算列预先计算出的值,因此在计算列上创建一个索引将会加快查询速度。

  提示:如果你想在计算列上创建索引,必须确保计算列上的公式不能包括任何“非确定的”函数,例如getdate()就是一个非确定的函数,因为每次调用它,它返回的值都是不一样的。

  创建索引视图

  你是否知道可以在视图上创建索引?OK,不知道没关系,看了我的介绍你就明白了。

  为什么要使用视图?

  大家都知道,视图本身不存储任何数据,只是一条编译的select语句。数据库会为视图生成一个执行计划,视图是可以重复使用的,因为执行计划也可以重复使用。

  视图本身不会带来性能的提升,我曾经以为它会“记住”查询结果,但后来我才知道它除了是一个编译了的查询外,其它什么都不是,视图根本记不住查询结果,我敢打赌好多刚接触SQL的人都会有这个错误的想法。

  但是现在我要告诉你一个方法让视图记住查询结果,其实非常简单,就是在视图上创建索引就可以了。

  如果你在视图上应用了索引,视图就成为索引视图,对于一个索引视图,数据库引擎处理SQL,并在数据文件中存储结果,和聚集表类似,当基础表中的数据发生变化时,SQL Server会自动维护索引,因此当你在索引视图上查询时,数据库引擎简单地从索引中查找值,速度当然就很快了,因此在视图上创建索引可以明显加快查询速度。

  但请注意,天下没有免费的午餐,创建索引视图可以提升性能,当基础表中的数据发生变化时,数据库引擎也会更新索引,因此,当视图要处理很多行,且要求和,当数据和基础表不经常发生变化时,就应该考虑创建索引视图。

  如何创建索引视图?

  1)创建/修改视图时指定SCHEMABINDING选项:

REATE VIEW dbo.vOrderDetails

  
WITH SCHEMABINDING

  
AS

  
SELECT

  2)在视图上创建一个唯一的聚集索引;

  3)视需要在视图上创建一个非聚集索引。

  不是所有视图上都可以创建索引,在视图上创建索引存在以下限制:

  1)创建视图时使用了SCHEMABINDING选项,这种情况下,数据库引擎不允许你改变表的基础结构;

  2)视图不能包含任何非确定性函数,DISTINCT子句和子查询;

  3)视图中的底层表必须由聚集索引(主键)。

  如果你发现你的应用程序中使用的TSQL是用视图实现的,但存在性能问题,那此时给视图加上索引可能会带来性能的提升。

  为用户定义函数(UDF)创建索引

  在用户定义函数上也可以创建索引,但不能直接在它上面创建索引,需要创建一个辅助的计算列,公式就使用用户定义函数,然后在这个计算列字段上创建索引。具体步骤如下:

  1)首先创建一个确定性的函数(如果不存在的话),在函数定义中添加SCHEMABINDING选项,如:

CREATE FUNCTION [dbo.ufnGetLineTotal]

  (

  
-- Add the parameters for the function here

  
@UnitPrice [money],

  
@UnitPriceDiscount [money],

  
@OrderQty [smallint]

  )

  
RETURNS money

  
WITH SCHEMABINDING

  
AS

  
BEGIN

  
return (((@UnitPrice*((1.0)-@UnitPriceDiscount))*@OrderQty))

  
END

  2)在目标表上增加一个计算列,使用前面定义的函数作为该列的计算公式,如图2所示。

CREATE FUNCTION [dbo.ufnGetLineTotal]

  (

  
-- Add the parameters for the function here

  
@UnitPrice [money],

  
@UnitPriceDiscount [money],

  
@OrderQty [smallint]

  )

  
RETURNS money

  
WITH SCHEMABINDING

  
AS

  
BEGIN

  
return (((@UnitPrice*((1.0)-@UnitPriceDiscount))*@OrderQty))

  
END
 

图 2 指定UDF为计算列的结算公式

  3)在计算列上创建索引

  当你的查询中包括UDF时,如果在该UDF上创建了以计算列为基础的索引,特别是两个表或视图的连接条件中使用了UDF,性能都会有明显的改善。

  在XML列上创建索引

  在SQL Server(2005和后续版本)中,XML列是以二进制大对象(BLOB)形式存储的,可以使用XQuery进行查询,但如果没有索引,每次查询XML数据类型时都非常耗时,特别是大型XML实例,因为SQL Server在运行时需要分隔二进制大对象评估查询。为了提升XML数据类型上的查询性能,XML列可以索引,XML索引分为两类。

  主XML索引

  创建XML列上的主索引时,SQL Server会切碎XML内容,创建多个数据行,包括元素,属性名,路径,节点类型和值等,创建主索引让SQL Server更轻松地支持XQuery请求。下面是创建一个主XML索引的示例语法。 

CREATE PRIMARY XML INDEX
index_name
ON <object> ( xml_column )

  次要XML索引

  虽然XML数据已经被切条,但SQL Server仍然要扫描所有切条的数据才能找到想要的结果,为了进一步提升性能,还需要在主XML索引之上创建次要XML索引。有三种次要XML索引。

  1)“路径”(Path)次要XML索引:使用.exist()方法确定一个特定的路径是否存在时它很有用;

  2)“值”(Value)次要XML索引:用于执行基于值的查询,但不知道完整的路径或路径包括通配符时;

  3)“属性”(Secondary)次要XML索引:知道路径时检索属性的值。

  下面是一个创建次要XML索引的示例:

CREATE XML INDEX
index_name
ON <object> ( xml_column )
USING XML
INDEX primary_xml_index_name
FOR { VALUE | PATH | PROPERTY }

  请注意,上面讲的原则是基础,如果盲目地在表上创建索引,不一定会提升性能,因为有时在某些表的某些列上创建索引时,可能会致使插入和更新操作变慢,当这个表上有一个低选中性列时更是如此,同样,当表中的记录很少(如<500)时,如果在这样的表上创建索引反倒会使数据检索性能降低,因为对于小表而言,全表扫描反而会更快,因此在创建索引时应放聪明一点。

2
相关文章