博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

表无索引为什么sp_spaceused 中的index_size不为0

Posted on 2009-10-18 22:23  nzperfect  阅读(2063)  评论(0编辑  收藏  举报

源于csdn论坛的一个提问:

CREATE TABLE TUser ( FName CHAR(8000), FAge INT, FSex bit )
INSERT INTO TUser
SELECT '张三',18,1 UNION ALL
SELECT '李四',20,1 UNION ALL
SELECT '王五',32,1 UNION ALL
SELECT '麻子',23,1

 
通过一个查询看下扫描的数据页:

SET STATISTICS IO ON
SELECT * FROM TUser
/*
(4 行受影响)
表 'TUser'。扫描计数 1,逻辑读取 4 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/

可以看到,该查询全部数据是扫了四个数据页,也就是说插入的四行数据,一行为一个page.

 但是我们执行下面的sql,发现index_size为8k:

EXEC sp_spaceused N'TUser'
/*
name    rows        reserved    data    index_size    unused
TUser    4              40 KB        32 KB    8 KB        0 KB
*/

 这是为什么,为什么没有建索引,这里却有一个index_size 8k ?

 

下面来看看index_size是怎么来的?
首先想到是的

sp_helptext sp_spaceused

通过查看sp_spaceused的代码,我们找到对于我们这个查询有用的信息代码:

 /*  
 ** Now calculate the summary data.   
 *  Note that LOB Data and Row-overflow Data are counted as Data Pages.  
 
*/
  
 
SELECT   
  
@reservedpages = SUM (reserved_page_count),  
  
@usedpages = SUM (used_page_count),  
  
@pages = SUM (  
   
CASE  
    
WHEN (index_id < 2THEN (in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)  
    
ELSE lob_used_page_count + row_overflow_used_page_count  
   
END  
   ),  
  
@rowCount = SUM (  
   
CASE  
    
WHEN (index_id < 2THEN row_count  
    
ELSE 0  
   
END  
   )  
 
FROM sys.dm_db_partition_stats  
 
WHERE object_id = @id;  
  
 
/*  
 ** Check if table has XML Indexes or Fulltext Indexes which use internal tables tied to this table  
 
*/
  
 
IF (SELECT count(*FROM sys.internal_tables WHERE parent_id = @id AND internal_type IN (202,204)) > 0   
 
BEGIN  
  
/*  
  **  Now calculate the summary data. Row counts in these internal tables don't   
  **  contribute towards row count of original table.    
  
*/
  
  
SELECT   
   
@reservedpages = @reservedpages + sum(reserved_page_count),  
   
@usedpages = @usedpages + sum(used_page_count)  
  
FROM sys.dm_db_partition_stats p, sys.internal_tables it  
  
WHERE it.parent_id = @id AND it.internal_type IN (202,204AND p.object_id = it.object_id;  
 
END  
  
 
SELECT   
  name 
= OBJECT_NAME (@id),  
  rows 
= convert (char(11), @rowCount),  
  reserved 
= LTRIM (STR (@reservedpages * 8150+ ' KB'),  
  data 
= LTRIM (STR (@pages * 8150+ ' KB'),  
  index_size 
= LTRIM (STR ((CASE WHEN @usedpages > @pages THEN (@usedpages - @pagesELSE 0 END* 8150+ ' KB'),  
  unused 
= LTRIM (STR ((CASE WHEN @reservedpages > @usedpages THEN (@reservedpages - @usedpagesELSE 0 END* 8150+ ' KB'

通过上面的代码,我们可以基于我们目前的这个情况(堆表,不含有xml和fulltext,所以上面的202/204那段不用管了)提练出index的page如下算法:

select index_pagecount=sum(used_page_count)-
sum(in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)
from sys.dm_db_partition_stats  
where object_id = object_id('TUser'); 

这个结果是1.再套上index_size的公式:

select index_size = LTRIM (STR ((1* 8150+ ' KB')
/*
8k
*/

这就是8k的算法,从下面联机文档上关于sys.dm_db_partition_stats 的解释,可以分析出这个8k是个IAM page.
used_page_count  bigint
用于分区的总页数。计算方法为 in_row_used_page_count + lob_used_page_count + row_overflow_used_page_count

上面我们算index_page时公式为:
sum(used_page_count)-
sum(in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)
也就是in_row_used_page_count -in_row_data_page_count
对于这两列,联机文档解释是:

in_row_used_page_count

用于存储和管理分区中的行内数据的总页数。该计数包括非叶 B 树页、IAM 页以及 in_row_data_page_count 列包含的全部页。

in_row_data_page_count

分区中存储行内数据所用的页数。如果分区是堆的一部分,则该值为堆中的数据页数。如果分区是索引的一部分,则该值为叶级别中的页数。(未计入 B 树中非叶页的数目。)以上两种情况都未计入 IAM(索引分配映射)页。

针对我们目前该表的情况,仅是一个堆表,那么可知前者是包含了IAM页,而后者不含有IAM页,那么sp_spaceused中的index_size在这里就是一个IAM(索引分配映射)页。

其实这个也可以通过DBCC IND看到:

如有错误,欢迎指正。