从统计数据表记录总数谈SQL_Server查询优化

   对于select count(*) , 倘若表中有多个索引,则SQL Server的处理与通常的查询语句不同.
在统计刻录总数时,聚集索引向来得不到数据库引擎的宠幸,除非WHERE子句中包含聚集索引
所在的字段. WHY? 我们知道,大量的磁盘I/O是非常耗时的. 扫描任何一个索引都可以计算
出刻录的总数,计算方式也是大致相同的,但非聚集索引的叶子层仅包含索引而没有每条记录
的具体数据,通常比聚集索引占用的页面要少的多,从而大大减少了磁盘I/O ,缩短了计算时间.
如果索引所在的字段包含在WHERE子句中,则仅需扫描部分叶子节点就能计算出总数,所需
时间会更短.
   由上而知若要优化select count(*) 或类似的语句,可考虑另外创建非聚集索引.如果select
count(*) 语句包含WHERE 子句或 Group by 子句,则索引最好建立在子句所引用的字段上.
当然数据库维护索引是需要代价的,设计时应通盘考虑.

   当数据表中的记录非常多时,不论怎样建立索引,查询记录总数都还是需要一定时间. 如果
我们不希望查询影响数据表的更新操作,则可以考虑使用WITH(NOLOCK) 查询提示.
如 select * from table WITH(NOLOCK) ,允许数据库引擎在统计过程中更新数据,或在更新
中途执行统计查询,牺牲查询结果的准确性换取并发性能. 顺便说一句,这里的NOLOCK并不
是什么数据都不锁,而是READUNCOMMITTED, 即允许读取可能会最终回滚(即放弃)的中间
数据.在SQL Server2005 中,如果启用了基于行版本控制的隔离级别,则WITH(NOLOCK)就
不需要了.数据库默认为已提交读隔离级别,可通过将READ_COMMITTED_SNAPSHOT 数据
库选项设置为ON来使用行版本控制, 如下:

ALTER DATABASE AdvertureWorks SET READ_COMMITTED_SNAPSHOT ON;

    如果我们只是希望简单得到表中记录的总数而不指定过滤条件,则无需执行
Select count(*) 语句, 调用sp_spaceused 存储过程几乎可立即得到结果.
如: EXEC  sp_spaceused 'table_name'  该存储过程返回包含多个字段的统计结果,其中
rows 字段即为记录总数. 这是由于默认情况下,数据库引擎自动更新表及索引的统计信息.
不过数据库管理员可以修改这一默认设置,如果您是数据库管理员,则可放心使用这个存储
过程. 在SQL Server2005中,您会发现sys.partitions 视图比 sp_spaceused存储过程更好用.

   如果数据表中包含自动增长字段,且在该字段上建有索引,我们还可以通过 比较该字段的最
大值和最小值来估算记录的大致数量,因为在有索引的字段上调用MAX()和MIN()不需要逐一
扫描数据页. 该方式可以指定其他过滤条件.
如 select max(id) +1 -min(id) from 'table' where country='cn'
如果自增列增量不为1,则统计结果减倍.
   注意SQL Server中有多种原因会导致表中实际的自动增长字段不完全是连续增长的.

posted @ 2011-06-01 16:15  俗雅冰山  阅读(1932)  评论(0编辑  收藏  举报