4.2 表的存储原理
4.2.1 内部存储概述
创建一个表,就会有一行或多行插入到用来管理这个表的多个系统表里。至少要写信息到sys.objects、sys.indexes和sys.columns这3个系统视图里,当新建的表有一个或多个外码约束时,相关的信息还会插入到sysrefrences系统视图里。对于每一个表来说,在sys.objects中都有单独一行描述这个表的基本信息,如表名、对象ID及表的所有者等。而对新表的每一列来说,sys.columns中都有一行描述相应的列,包括列名、列的数据类型和长度等。每个列也有一个列ID,即colid,它直接对应在建表时所指定的列的顺序。也就是说,在CREATE TABLE语句列出的第1列的列ID为1,在该语句列出的第2列的列ID为2,等等。
比如创建下面的课程表:
CREATE TABLE 课程表 ( 课号 CHAR (6) NOT NULL , 课名 CHAR (20) NOT NULL , 教材名称 CHAR (20) NULL , 编著者 CHAR (10) NULL , 出版社 CHAR (20) NULL , 版号 CHAR (15) NULL , 定价 MONEY NULL, PRIMARY KEY(课号) )
图4-1显示了创建课程表后sysobjects、syscolumns和sysindexes中增加的信息。
·sysobjects主要记录新表的基本信息,如表名、对象ID及表创建的时间等。
·syscolumns主要记录新表列的信息,如列名、类型和长度等。
·sysindexes主要记录指向新表所使用的存储空间的指针和主键名称等信息。
图4-1 系统表关于新用户表创建的记录
4.2.2 SQL Server数据记录结构
(1)定长记录的存储
首先来看最简单的情况,记录中所有字段都是定长的:
CREATE TABLE fixed ( col1 INT NOT NULL col2 CHAR(5) NOT NULL col3 CHAR(3) NULL col4 FLOAT NOT NULL )
当这个表被创建以后,就有类似下面的一个记录被插入到sysindexes系统表中:
而其各个字段则会被插入到syscolumns系统表中:
当往fixed表中插入一个记录数据时,例如:
INSERT INTO fixed VALUES(123,'ABCD',NULL,45.5)
fixed表在sysindexes系统表里的内容就会发生变化:
这说明,在插入了一个记录数据之后,SQL Server就为fixed表分配了一个数据页。fixed表只包含4个定长字段,sysindexes表中minlen字段的值表示记录的最小长度,该长度恰好是syscolumns表中表示字段长度的length的数字之和再加上4字节。其中,额外的4字节是用于记录字段数目的2字节和表示字段中NULL的字节数。
在fixed表中插入一条记录后,这个记录在数据页的实际内容如图4-2所示。
图4-2 一个只包含定长字段的数据行
第1字节是状态位A,它的值是0x10,表示只有位4是1,其他位都是0,因此该记录没有变长字段(如果位5为1,说明存在变长字段)。第2字节在记录中未用。第3和第4字节(1800)表示所有定长字段的长度,交换字节是0x0018,它就是十进制24。字段col1的数据从偏移量4开始;字段col2的数据从偏移量8开始;字段col3的数据从偏移量13开始;字段col4的数据从偏移量16开始。作为一个整数,字段col2中的数据7b000000必须交换字节变成0x0000007b,该值是十进制的123。字段col3中的数据3字节全是0,说明该列是一个真正的NULL。偏移量24为起始的2字节是0400,交换字节后是0x0004,表示该记录有4个字段。最后1字节是NULL位图,其值4意味着只有第3位是1,表示第3个字段是NULL。
需要强调的是,定长记录总是用满在表中定义的字节数,即使某个字段的值是NULL,这是和变长字段记录的根本不同。
(2)变长字段记录的存储
下面是有变长字段的情况,记录中有3个字段是变长的。
CREATE TABLE variable ( col1 CHAR(3) NOT NULL col2 VARCHAR(15) NOT NULL col3 VARCHAR(5) NULL col4 VARCHAR(10) NOT NULL col5 SMALLINT NOT NULL )
当这个表被创建以后,就有类似下面一个记录被插入到sysindexes系统表中:
而其各个字段则会被插入到syscolumns系统表中:
当往variable表中插入一个记录数据时,例如:
INSERT INTO variable VALUES('xyz', 'ABCDe',NULL, '123',999)
variable表在sysindexes系统表里的内容就会发生变化:
定长字段的数据位于记录中由syscolumns的xoffset值指定的字节偏移量所在的位置,即col1起始于字节偏移量4的位置,而col5起始于字节偏移量7的位置,如图4-3所示。
图4-3 一个包含变长字段的数据行
为了找到变长字段,首先要确定记录中列偏移数组的位置。在表示总字段数的2字节(其值是0500)和表示位图的1字节(其值为04)之后,就是变长字段数的2字节,在本例中其值是0300,换算成十六进制数是0x003,换算成十进制数是3,说明该记录有3个变长字段存在。紧跟其后的字节就是变长字段偏移数组。该例变长字段偏移数组用3个2字节来表示3个变长字段在记录中的结束位置。1900经过字节交换是0x0019,所以第1个变长字段结束于25字节处。接下来也是0x0019,所以第2个变长字段实际长度为0,表明没有任何东西存储在变长数据区域。所以,可见变长字段具有NULL值,在记录中是不占用任何空间的。SQL Server根据位图中相应位是0还是1,来区分一个变长字段的值是NULL还是空串。1c00经过字节交换是0x001c,所以第3个变长字段结束于28字节处,而且整个记录也结束于28字节处,换句话说,目前整个记录的实际长度是28字节。
需要注意的是,这个长度和上面表定义的记录的长度并不一样,这正说明了可变长字段是根据当前存入的具体数据长度确定存储长度。最后,千万不要忘记,整个记录的开销还包括页的底部用于每行的行偏移数组的2字节。