--chenjing957翻译整理,转贴请注明出自微软BI开拓者www.windbi.com--原文为《SQL性能分析(英文版)》
在T-SQL语句中会隐藏有能给我们带来性能上提升的重要的语句吗?实际上除了一些技巧之外是不可能的,所以最好在你写代码的时候把这些技巧记在脑袋里面,这样你写的语句将会获得更好的性能。
经常使用where子句:
这项建议看上去可能会觉得是一项很普通的事情,但是实际当中你可能会因为发现很多都是返回所有行的查询语句而惊讶。可怜的SQL Server拼命努力地工作为他们返回的所有数据行,却被拿到客户端后通过大肆处理最后过滤出自己想要的那么一点点数据。
另外,通常在SQL Server中都应该尽可能地使用where字句来返回最少的数据。并且你要记住,如果你没有用where子句,SQL Server只会愚蠢地进行全表的扫描操作,而索引绝大多数情况下都是在where子句中被激活后并返回最少行。
在我的编写代码的时候会试着避免使用那些没有SARG的where语句,习惯SARG的DBA通常呼吁在查询中包含一个固定的值。有一个团队源于SARG,这也是他们研究争论的焦点。呵呵,甚至没有人会说DBA花了大量的时间在漂亮的词句上。
基本上来说是这样的:一个where子句如where customer ID < 10 是SARG的,因为它包含一个固定的值用来比较,这个查询的优点在于能够让where子句尽可能的应用索引来使得查询运行得更快。因此在where子句中不能包含:
1、<>,!=,!>,!<操作符
2、用IS NULL作比较
3、NOT,NOT EXISTS,NOT IN,NOT LIKE和LIKE操作符,实际上LIKE操作符是可SARG的,只要你在使用的时候不要用通配符作为第一个字母(例如:like "A%"就是可SARG的,而LIKE "%A"就不是)
4、任何包含如SUM(ordertotal)列的函数
5、操作符两边都有相同的列表达式,如customertotal = customertotal + 5
这些并不会完全阻止优化器使用索引,但是最好能避免使用它们。然而,优化器有足够的智能来使用索引,在where子句中有些可能是可SARG的表达式,而有些至少看上去并不是可SARG的,这时候,优化器就可能会进行一些处理然后把它们再视作可SARG表达式。
如果你想在自己的select语句中小心的指定一些列,并且这些列存在索引,优化器将使用索引而不会执行较慢的全表扫描。记住,在select语句中一个索引对应一条所指定的列。
下面写一个不能够当作SARG的的查询语句,例如:
where substring(customername,1,2) = "do"
完成的工作是返回所有名字开头为"do"的消费者,它将执行全表扫描来做,因为函数是不能SARG的,你可能会改写为:
where customername like "do%"
这就是能够SARG的查询语句了,允许查询优化器使用建立在customername列上的索引(如果存在这个索引的话)来立即准确的查找出名字开头为"do"的所有数据.
避免使用游标:
从性能的角度上看,游标是一个很可怕的东西.不得不承认,他们有时候看上去很像完成工作的唯一方式,但并不总是正确的.考虑下面一个来自
http://www.sql-server-performance.com的一个例子:
DECLARE @LineTotal money
DECLARE @InvoiceTotal money
SET @LineTotal = 0
SET @InvoiceTotal = 0
DECLARE Line_Item_Cursor CURSOR FOR
SELECT UnitPrice*Quantity
FROM [order details]
WHERE orderid = 10248
OPEN Line_Item_Cursor
FETCH NEXT FROM Line_Item_Cursor INTO @LineTotal
WHILE @@FETCH_STATUS = 0
BEGIN
SET @InvoiceTotal = @InvoiceTotal + @LineTotal
FETCH NEXT FROM Line_Item_Cursor INTO @LineTotal
END
CLOSE Line_Item_Cursor
DEALLOCATE Line_Item_Cursor
SELECT @InvoiceTotal InvoiceTotal
这段代码能够很容易在发货单中定位(一共10248行),添加并且得出总量.游标通常在发货单中一步一步获取每行的信息并且添加它价格到@linetotal变量里。然而在下面的例子将更容易作出类似的处理,但没有使用游标:
DECLARE @InvoiceTotal money
SELECT @InvoiceTotal = sum(UnitPrice*Quantity)
FROM [order details]
WHERE orderid = 10248
SELECT @InvoiceTotal InvoiceTotal
这段新的代码使用了SQL Server的集合函数来计算出同样的信息,但它就那么简单几行代码而且没有用到游标这个资源杀手。比起更加复杂的游标操作来,这些集合函数能够以最快的时间计算并返回同样的结果。
其他的T-SQL语句的技巧:
注意,有些技巧并不适合任何类型的语句,下面的一些提示是额外的需要我们在使用T-SQL的时候来避免.
1、DISTINCT子句通常用来返回对应列中不重复的数据,在使用之间应该先问问自己:重复数据对你来说是否真的是一件很糟糕的事情。在SQL Server里DISTINCT子句通常要消耗额外的运行时间,并且如果不用它的话你将会获得更好的性能。
2、我曾经提到过,在查询的时候只查询自己需要的有限行数据,特别是在你能查找的时候一定要指定你真正需要的列,因而绝不要盲目使用:select * 。这样做你将会减少SQL Server服务器不得不传输的数据量。你其实还能够让SQL Server服务器有更好的有机会来使用索引,当然前提是你所要查询的信息必须受到一定限制。
3、还有一种好的方式能够让你的查询返回有限个行数,就是连同select一起使用top keyword。top允许你指定返回的最大行数。SQL Server仍然在执行你在where中所指定的查询,但是一旦发现达到了指定的行数便会停止处理,保存处理时间。你可以用select to 100来返回100行结果或者用select top 10 percent来决定返回结果集的百分比。这些你可以根据需要自行选择,总之你可以用set rowcount命令来限制你所查询的全部结果中被返回的行数。