《Microsoft Sql server 2008 Internals》读书笔记订阅地址:
http://www.cnblogs.com/downmoon/category/230397.html/rss
《Microsoft Sql server 2008 Internals》索引目录:
《Microsoft Sql server 2008 Internal》读书笔记--目录索引
上篇文章中提到五种典型的存储结构:一、固定长度的行;二、可变长度的行;三、Null和可变长度列;四、时间和日期数据;五、SQL_variant 数据。今天我们继续来看可变长度的行的存储:可变长度的列要比固定长度的列的存储复杂一些。
我们先来测试一个表Variable,有三个variable 列和两个固定长度列:
CREATE
TABLE
variable
(
Col1
char
(
3
)
NOT
NULL
,
Col2
varchar
(
250
)
NOT
NULL
,
Col3
varchar
(
5
)
NULL
,
Col4
varchar
(
20
)
NOT
NULL
,
Col5
smallint
NULL
);
SELECT
object_id
, type_desc,
indexproperty
(
object_id
, name,
'
minlen
'
)
as
minlen
FROM
sys.indexes
where
object_id
=
object_id
(
'
variable
'
);
SELECT
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
(
'
variable
'
);
可以看到:
现在,插入一行数据:
INSERT
variable
VALUES
(
'
AAA
'
,
REPLICATE
(
'
X
'
,
250
),
NULL
,
'
ABC
'
,
123
);
说明:REPLICATE函数仅仅填充了250个X到col2,很有用的一个函数哟,特别在填充测试数据的时候。
我们可以按照前一篇所述的方法来查看data pages的存储状况:
简要说明:
1、还记得前面学过的存放顺序吗?先固定列,再可变列。所以Col1和Col5要先存储,并且偏移量为正值,而col2\Col3\col4偏移量分别是-1,-2,-3。正好表示它们分别是可变列的第一、第二、第三个序号。
2、0500表示该行有5列。
3、04代表null Bitmap没有使用。
4、0300代表共有三个可变长度的列。
5、0e01代表第一个可变列的结束位置,与第二个可变列的位置相同。这是为什么呢?可能有人猜出来了,因为插入的是Null,所以实际上并没有数据存储到行中。这与固定长度的行不同。固定长度的行,Null值也占用存储空间。
6、该行数据的总长度为273字节,计算方法是0X1101,经过字节换算,应该是0X0111=273
那么,存储这行数据到底用去多少空间呢?可变长度列意味着更大的开销(overhead),它们的实际长度是无法预测的。即便对于固定长度的列,开销的数量也会取决于表中列的数量。记住:Null bitmap必须有足够的空间来存放每列的bit值。此外,每行还必须包含2个字节overhead和行底部的行偏移量数组
再来看第三种存储:Null和可变长度列
可能,有人看了上面的图会说,可变长度的列既然并不存放实际数据,那么应该不会占用空间。可为什么每个可变列还是有2个字节的偏移呢?这个-2是从哪儿来的呢?
因此,我们不能说SQL Server一点空间也不用。实际上,它仍然用了两个字节来存放了偏移数组。我们看一个例子:
CREATE
TABLE
dbo.null_varchar
(
id
INT
PRIMARY
KEY
IDENTITY
(
1
,
1
),
col1
VARCHAR
(
10
)
NULL
,
col2
VARCHAR
(
10
)
NULL
,
col3
VARCHAR
(
10
)
NULL
,
col4
VARCHAR
(
10
)
NULL
,
col5
VARCHAR
(
10
)
NULL
,
col6
VARCHAR
(
10
)
NULL
,
col7
VARCHAR
(
10
)
NULL
,
col8
VARCHAR
(
10
)
NULL
,
col9
VARCHAR
(
10
)
NULL
,
col10
VARCHAR
(
10
)
NULL
);
GO
SET
NOCOUNT
ON
INSERT
INTO
null_varchar(col10)
SELECT
'
a
'
;
INSERT
INTO
null_varchar(col1)
SELECT
'
b
'
;
INSERT
INTO
null_varchar
SELECT
''
,
''
,
''
,
''
,
''
,
''
,
''
,
''
,
''
,
'
c
'
;
INSERT
INTO
null_varchar
SELECT
'
d
'
,
''
,
''
,
''
,
''
,
''
,
''
,
''
,
''
,
''
;
GO
从红色部分显示,0x03fe意味着0000001111111110,从右往左看,第一列是not Null,第10列是not Null,其余列是Null。共11列,后5列被忽略了。
同理:0x07fc意味着0000011111111100,从右往左看,第一列和第二列是not Null,其余列是Null。
第四种存储方式:日期和时间格式
CREATE
TABLE
times (
a
char
(
1
),
dt1
datetime
,
b
char
(
1
),
sd
smalldatetime
,
c
char
(
1
),
dt2 datetime2,
d
char
(
1
),
dt date,
e
char
(
1
),
dto datetimeoffset,
f
char
(
1
),
t time,
g
char
(
1
),
t0 time(
0
),
h
char
(
1
),
t1 time(
1
),
i
char
(
1
),
t2 time(
2
),
j
char
(
1
),
t3 time(
3
),
k
char
(
1
),
t4 time(
4
),
l
char
(
1
),
t5 time(
5
),
m
char
(
1
),
t6 time(
6
),
n
char
(
1
),
t7 time(
7
));
GO
INSERT
INTO
times
SELECT
'
a
'
,
'
01:02:03.123
'
,
'
b
'
,
'
01:02:03.123
'
,
'
c
'
,
'
01:02:03.123
'
,
'
d
'
,
'
01:02:03.123
'
,
'
e
'
,
'
01:02:03.123
'
,
'
f
'
,
'
01:02:03.123
'
,
'
g
'
,
'
01:02:03.123
'
,
'
h
'
,
'
01:02:03.123
'
,
'
i
'
,
'
01:02:03.123
'
,
'
j
'
,
'
01:02:03.123
'
,
'
k
'
,
'
01:02:03.123
'
,
'
l
'
,
'
01:02:03.123
'
,
'
m
'
,
'
01:02:03.123
'
,
'
n
'
,
'
01:02:03.123
'
;
需要说明的是:
对于datatime和smalldatetime,存储值为0意味着日期是'1900-01-01",对于其他类型的日期值为693595意味着日期是"0001-01-01"
你可以通过这个语句查看对应的日期:
SELECT
DATEADD
(dd,
693595
,
CAST
(
'
0001/1/1
'
AS
datetime2));
结果为默认值:1900-01-01 00:00:00.0000000
第五种:SQL_variant 数据
在此略去。
关于存储行更多的信息,在后面的第七章学习中将继续展开。
下一节将继续学习列和行的存储相关操作。