高效SQL查询之索引覆盖(index coverage)
今天做SQL 优化,查找执行计划时,执行计划,发现此执行计划与以往的计划有所区别;找录互联网,终于找一篇有关研究比较深入的文章;
原执行计划使用的是索引扫描,突然一下会使用索引覆盖技术,效率大增;
SELECT * 的真相:索引覆盖(index coverage)
SELECT *的效率很糟糕吗?当然,所有人都知道这一点,但是为什么呢?
是因为返回了太多的数据?
这是一个普遍的回答,但我不这样认为。如果你的数据库设计规范合理,那么带宽占用实际上非常的小。
让我们看看下面的例子。下面的查询将会从AdventureWorks.dbo.TransactionHistoryArchive(总共大约有近9万行数据)中选择出326行数据。第一个使用了SELECT * 查询,后一个查询则有明确的字段。
SELECT * from Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100
SELECT ReferenceOrderLineID from Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100
在这种情况下,两者在网络带宽的区别只有15K(180K-165K),大约10%的带宽差异。的确值得去优化,但不会有很大的效果。
SELECT * 将造成表/索引扫描
SELECT * 的最大问题是将影响查询计划。SQL Server主要使用索引去查询你需要的数据,当索引包括所有的你请求查询的字段,SQL Server将不需要去在表中查询。这个概念称做索引覆盖。在上面的例子中,第一个查询结果是在聚集索引扫描中,反过来,第二个例子使用了更多更有效率的索引扫描。在这个案例中,索引扫描比聚集索引扫描快100倍 。
除非你已经将为每个字段建立了索引(显然不是个好主意),SELECT *是不能够利用到索引覆盖,你只能去做扫描操作(非常的没有效率)。
如果你只是查询你所需要的字段,那你更可能的覆盖到你的索引。我想这就是不推荐使用SELECT *的主要的原因。
稳定性方面
在维护一个应用程序时,SELECT *也会带来一些意想不大的问题。它会引起你的代码发生一些不确定性。如果你增加了一个行(译注:我觉得这里应该是字段)到一个表中,那么SELECT * 返回的结果到你的应用程序中将会在结构上发生变化。良好的应用程序应该是使用字段名称的,而不应该受此影响。当外界发生变化时,良好的应用程序设计也应该最小化的更改。
英文原稿: http://weblogs.asp.net/jgalloway/archive/2007/07/18/the-real-reason-select-queries
相关文档:
Create PROCEDURE [dbo].[PR_addRoles]
@RolesID INT,
@Roles varchar (100),
@ID INT OUT
AS
BEGIN TRY
BEGIN TRAN
INSERT FS_Roles (RolesID,Roles) VALUES (@RolesID,@Roles)
COMMIT TRAN
SET @ID=1
END TRY
BEGIN CATCH
ROLLBACK TRAN
SET @ID=0
END CATCH
......
varchar和nvarchar如何选择:
varchar在SQL Server中是采用单字节来存储数据的,nvarchar是使用Unico来存储数据的。中文字符存储到SQL Server中会保存为两个字节(一般采用Unico编码),英文字符保存到数据库中,如果字段的类型为varchar,则只会占用一个字节,而如果字段的类型为nvarchar,则会占用两个字节。虽然使用nva ......
新建学生-课程数据库的三个表:
学生表:Student(Sno,Sname,Ssex,Sage,Sdept) Sno为主码;
课程表:Course(Cno,Cname,Cpno,Credeit) Cno为主码;
学生选修表:SC(Sno,Cno,Grade) Sno,Cno,为主码;
Student
学号
Sno姓名
Sname性别
Ssex年龄
Sage所在系
Sdept
95001李勇男20CS
95002刘晨女19IS
95003王敏女18MA
95004 ......
大型系统的生产环境,一般情况下,我们评价一条查询是否有效率,更多的是关注逻辑 IO( 至于为什么,回头补一篇 ) 。我们常说,“要建彪悍的索引”、“要写高效的 SQL ”,其实最终目的就是在相同结果集情况下,尽可能减少逻辑 IO 。
1.1 where 条件的列上都得有统计信息。
......
我们先看 NestedLoop 和 MergeJoin 的算法(以下为引用,见 RicCC 的《 通往性能优化的天堂 - 地狱 JOIN 方法说明 》 ):
==================================
NestedLoop:
foreach rowA in tableA where tableA.col2=?
{
search rowsB from tableB where tab ......