当我们想统计数据表的记录总数时,我们使用的T-SQL函数count(*) 。如果在一个包含了数百万行的大表中执行这个函数的话,,可以要花很长时间才能返回整个表的记录总数,这导致了查询性能的下降。

  一、常规办法:采用Count ()函数

  每个数据库管理员知道如何使用count(*) 函数。SQL Server在执行这个函数时,为了返回总表的行计数,需要对索引/表进行完整的扫描。因此建议DBA们尽量避免针对整个表使用聚合函数count(*),因为它影响了数据库的性能。

  下面我们来看个AdventureWorks数据库中的例子。

  在查询分析器中执行下面的查询语句:

 


  use AdventureWorks
  go
  select count (*) from Sales.SalesOrderDetail

  查询分析器执行后,显示有121317行。

  l 第一步中对整个表进行索引扫描,这是个非常耗时的过程(占81%)。

  l 第二步中应用流聚合也较耗时(占19%)。

 二、新方法:采用 row_count ()自定义函数

  在SQL Server 2005中的两个对象目录视图:sys.partitions和sys.allocation_units包含了这些信息,可以用来获取整个表的总记录数。

  sys.partitions 目录视图

  sys.partitions视图包含了数据库中每个连续分区的所有表和索引。SQL Server 2005中的所有表格和索引,即使他们没有明确划分,也包含这个视图的至少一个分区中。

  数据库中所有表和索引的每个分区在表中各对应一行。即使 SQL Server 2005 中的所有表和索引并未显式分区,也认为它们至少包含一个分区。

  在新的实现方法中,我们用到了sys. partitions 视图中的以下字段:

 

字段名称
数据类型
描述
partition_id
bigint
分区的 ID。在数据库中是唯一的。
object_id
int
此分区所属的对象的 ID。每个表或视图都至少包含一个分区。
index_id
 Int
此分区所属的对象内的索引的 ID。
0表示是堆表中
1表示是集群表
rows    
bigint
此分区中的大约行数。

  sys.allocation_units 目录视图

  sys.allocation_units视图包含了数据库中一个连续为每个分配单元。

  数据库中的每个分配单元都在表中占一行。

  在新的实现方法中,我们用到了sys.allocation_units视图中的以下字段:

 

字段名称
数据类型
描述
container_id
bigint
与分配单元关联的存储容器的 ID。
如果 type = 1 或 3,则 container_id = sys.partitions.hobt_id。
如果 type 为 2,则 container_id = sys.partitions.partition_id。
0 = 标记为要延迟删除的分配单元
Type
tinyint
分配单元的类型。
0 = 已删除
1 = 行内数据(所有数据类型,但 LOB 数据类型除外)
2 = 大型对象 (LOB) 数据(text、ntext、 image、xml、大型值类型以及 CLR 用户定义类型)
3 = 行溢出数据

  在row_count这个新的用户自定义函数中,[sys.partitions]视图与[sys.allocation_units]视图相关联,并按照以下几个条件进行过滤:

  l [sys.allocation_units].type = 1,所读取的行数据,不包含text, ntext, image等类型的大型对象。

  l [sys.partitions].index_id 属于0或者1 : 0表示是堆表中,1表示是集群表。

  l [sys.partitions].row不为空。

  由于row_count这个自定义函数需要在每一个数据库中执行,因此将权限设置为Public。

  用户自定义函数row_count具体描述,见下一节。