SQL Server 2005 int与datetime数据类型的存储结构说明

我经常被问到,或者在论坛上看到,有人提出这样的疑问:为什么SQL Serverint型存的是固定长度4个字节,但实际上能存储远远超过4长度的整型,例如123456789;而日期型是固定长度8个字节,而实际上存储的是类似于” 2011-04-18 20:32:12.540”这种字符长度加起来远远超过8个字节的数据?看起来似乎是一个矛盾的,不可思议的现象。如果你刚好也有这样的疑问,那这篇文章将会回答您的问题。

 

事实上,这是把int123456789varchar(或者char)型的”123456789”混为一谈。是的,他们看起来是一样,并且还可以显式/隐式地相互转换,但实际上,SQL Server存储int型与字符型的数据类型,用的是完全不一样的存储结构。同理,存储datetime型与字符型也是完全不同的。本篇文章通过探索SQL Server 2005存储结构,来说明int型与datetime型是如何被存储的。

 

:本文示范数据库所使用的版本为SQL Server 2005.

 

首先在测试数据库(我的数据库名为Jelly)中建一个测试表test,并插入两条数据:

create table test (

c1 int,                            --c1int型,存储长度为个字节

c2 datetime default(getdate())     --c2datetime型,存储长度为个字节

)

 

insert into test(c1) values(1)

insert into test(c1) values(53876254)

 

select * from test

 

SELECT结果如下:

 

 

 

SQL Server以页为单位存储数据。一个页面的大小是8KB。毫无疑问,上面我们所建的这个test表,一个页面完全可以装得下。

 

--使用未文档化的指令DBCC IND找出该表的第一个数据页(返回的结果集中PageType=1那一行)

DBCC IND ('jelly', 'test', -1);

GO

 

以上DBCC指令输出的结果如下:

 

 

 

请定位到PageType=1那行。PageType=1代表这是个数据页(data page)PageFID1,意思是该数据页位于文件号(file number)1的那个数据文件上;PagePID值为174,指的是该数据页的页编号(page number)。文件号+页编号就能唯一确定SQL Server数据库的一个数据页。

 

接下来使用DBCC PAGE指令输出该页的内容:

--唯有开启追踪旗标3604DBCC PAGE才能将结果输出到客户端。否则将没有结果集返回。

dbcc traceon (3604)

 

--DBCC PAGE的选项3意为输出最详细的页面内容。

dbcc page(jelly,1,174,3)

 

DBCC PAGE的输出分为三部分:Buffer,PAGE HEADER,以及详细页面数据信息。限于篇幅,我这里只截取了我们今天要详细探讨的DBCC PAGE第三部分的截图。你在结果集输出窗口向下拖动滚动条就可以找到类似的输出。

 

 

 

 

截图中绿色框框标识部分为test表中cl=1的数据行内容;紫色框框标识部分为c1=53876254的数据行内容。

 

红色细框标识部分为每行的dbcc page输出,以十六位的格式显示,每两位代表一个字节。需要特别指出的是,在转换dbcc page十六进位的数据格式之前,需要先以字节为单位,将这些十六进位的数据反转才能读出正确的数据。以0x12c02501为例,读取的方式是这样的:

先以字节为单位将数据分开:12 c0 25 01,然后反转成:01 25 c0 12,这时得才正确的十六位值:0x0125c012.

 

Test表是定长字段的表,也就是说这个表没有可变长度字段, 所以它的存储结构说明如以下excel表格所示:(对于含有可变长度字段的表,存储结构是不一样的。这超出了本文讨论范围。有兴趣者可参考本博客中另一篇<<SQL Server存储结构>>的文章,需要指出的是,那是sql server 2000的,到2005已经略有调整。)

 

 

 

 

说明:

a.       字节0与字节1SQL Server内部定义的固定结构。

b.       字节2~3表示所有定长字段的位置。SQL Server总是将所有定长字段放在一块儿存储。那么16的值是怎么得来的呢?这是:字节0的长度1+字节1的长度1+字节2~3的长度2+定长字段的总长度12(int 4字节+datetime 8字节)=16.

因此从字节2~3的数据中SQL Server可以知道,该表的定长字段总长度为16-(1+1+2)=12

至于SQL Server如何从这两个字节的定长字段总长度数字16,区分出每个定长字段的长度,请见本文附录。

c.       字节4~7为存储int型字段值的位置:

excel中所示的0x00000001c1中的1

同样的,第二行,c1=53876254 DBCC PAGE的输出,对应的字节4~7的值为:0x0336161e.转换为十进制的值正好为53876254

d.       字节8~11为存储datetime型字段时间部分值(::.毫秒)的位置;

e.       字节12~15为存储datetime型字段年份部分值的位置。

 

 

以下为SQL Server 2005 Books Online中关于datetime型的存储说明:

SQL Server 2005 数据库引擎用两个 4 字节的整数内部存储 datetime 数据类型的值。

第一个 4 字节存储“基础日期”(即 1900 1 1 日)之前或之后的天数。基础日期是系统参照日期。

另外一个 4 字节存储天的时间,以午夜后经过的 1/300 秒数表示。

由此可知:

a.       字节8~11的十进制值19251218表示,自从当天的00:00:00.000开始,到字段值所示的时间为止,经过了多少个1/300秒;

b.       字节12~15的十进制值40649表示,自从190011日开始,到字段值所示的日期为止,经过了多少天。

 

 

至止,你应该明白int/datetime型是如何存储的了,也终于明白为什么四个字节的int型能存储超过四个字符长度的数字的原因了。

 

以上说明也同样合适于smallint,tinyint以及smalldatetime

 

 

附录:

如下查询返回的结果集leaf_offset字段,说明test资料表每个字段的偏移位。SQL Server存储引擎以类似的方式获得资料表每个定长字段的偏移信息。

SELECT  c.name AS column_name, column_id, max_inrow_length, 

         pc.system_type_id, leaf_offset 

 FROM sys.system_internals_partition_columns pc

    JOIN sys.partitions p 

      ON p.partition_id = pc.partition_id

    JOIN sys.columns c

         ON column_id = partition_column_id 

            AND c.object_id = p.object_id

WHERE p.object_id=object_id('test');

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值