sqlserver 字符串判空_SQLSERVER NULL和空字符串的区别 使用NULL是否节省空间

本文探讨了SQLSERVER中varchar和char类型在存储NULL和空字符串时的空间占用。实验结果显示,varchar类型的NULL和空字符串都不占用空间,而char类型无论NULL或空字符串都会占用与定义长度相同的空间。结论是空间占用取决于数据类型而非是否为空值。
摘要由CSDN通过智能技术生成

这里只讨论字符串类型,int、datetime、text这些数据类型就不讨论了,因为是否节省空间是根据数据类型来定的

在写这篇文章之前,本人一直以为这个问题很简单的,看一下数据页就行了,但是后来写着写着,也修改了几次

发现需要对SQSERVER的数据页内容很熟悉您才能知道SQLSERVER内部空间占用是怎样的,希望大家在继续往下看之前先看一下下面文章

在往下看之前请各位先看一下下面的文章

char nchar varchar nvarchar的区别  :char nchar varchar nvarchar数据类型所占用长度

如果不看上面的文章,对于刚入门的人来说可能只会是一知半解,为了文章的篇幅不要过长,我就在文章里不解释一些重要名词了

大家看一下给出的文章就可以了o(∩_∩)o

先建立下面表格并插入测试数据

8f900a89c6347c561fdf2122f13be562.png View Code

f9ee181c8d4f98fc7d8e6cb5e9366320.png

建立一个DBCCResult表,保存DBCC IND的结果

8f900a89c6347c561fdf2122f13be562.png View Code

查看各张表的情况

VARCHAR类型的情况

testnullvarchar表

8f900a89c6347c561fdf2122f13be562.png View Code

数据页内容

8f900a89c6347c561fdf2122f13be562.png View Code

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 Slot 0 Offset 0x60 Length 11

2

3 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

4 Memory Dump @0x0A16C060

5

6 00000000: 10000800 01000000 0200fe†††††††††††††...........

7

8 Slot 0 Column 0 Offset 0x4 Length 4

9

10 id = 1

11 NAME = [NULL]

12

13 Slot 1 Offset 0x6b Length 17

14

15 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS

16

17 Memory Dump @0x0A16C06B

18

19 00000000: 30000800 02000000 0200fc01 001100c4 †0...............

20 00000010: e3†††††††††††††††††††††††††††††††††††.

21

22 Slot 1 Column 0 Offset 0x4 Length 4

23

24 id = 2

25

26 Slot 1 Column 1 Offset 0xf Length 2

27

28 NAME = 你

48304ba5e6f9fe08f3fa1abda7d326ab.png

d266c9ac80093d64ce0ff7e36b680adb.png

3ce680b29b9bb0289f5e6586d9c84721.png

我们看第一行记录长度11怎麽得出来的

01605751ddc22da85f14af6622fe6295.png

2个字节行标头存储了状态A和状态B的信息(2 bytes row header)

2个字节存储固定长度大小,因为一行记录了有varchar这些不固定长度的数据类型(2 bytes for length of fixed length columns)

SQLSERVER需要知道int、datetime、decimal这些固定长度数据类型的大小

2个字节的列数,用来存储这个表一共有多少列(2 bytes for number of columns in the table)

1个字节的null bitmap,(1 byte for null bitmap)

4个字节存储int型数据(4 bytes for int (1st column))

2+2+2+1+4=11

换言之,第一行记录中name字段不占用任何空间,因为第一行记录中的name值为NULL

30f053ab0fd9fc2ced37432f456399b6.png

-------------------------------------------------------------------------------------------------------

7ea013ec286b3cd9ad70a16b75ecb486.png

我们看第二行记录长度17怎麽得出来的

2个字节行标头存储了状态A和状态B的信息(2 bytes row header)

2个字节存储固定长度大小,因为一行记录了有varchar这些不固定长度的数据类型(2 bytes for length of fixed length columns)

SQLSERVER需要知道int、datetime、decimal这些固定长度数据类型的大小

2个字节的列数,用来存储这个表一共有多少列(2 bytes for number of columns in the table)

1个字节的null bitmap,(1 byte for null bitmap)

4个字节存储int型数据(4 bytes for int (1st column))

2个字节存储数据行中的可变长度列数量,统计数据行中一共有多少列是nvarchar ,varchar类型的列( 2 bytes for number of variable length columns in the table)

2个字节存储可变长度偏移阵列,可变长度偏移阵列的公式

5a27c77959ffa7a855a5ceeefe8b98d2.png

2*表格中可变长度数据类型的列数量,这个表只有一列varchar,所以2*1=2,为什麽要有可变长度偏移阵列?我估计是因为可变长度的数据类型

存储的数据是不固定的,所以要预留一些位置,当update varchar值的时候有足够的位置(2 bytes for name column offset)

2个字节存储name列的值,为什麽用两个字节大家可以看一下

2+2+2+1+4+2+2+2=17

前11个字节跟第一行记录是一样的长度,关键在于后面的6个字节,在这6个字节中只有2个字节实际存储数据的

为什麽在第一行记录里没有这4个字节呢?

2个字节存储数据行中的可变长度列数量

2个字节存储可变长度偏移阵列

想法:

我估计是因为,第一行记录中没有一个可变长度数据类型的列是有数据的,全部都是NULL,

既然这样SQLSERVER就没有必要再用4个字节去存储2个字节存储数据行中的可变长度列数量和2个字节存储可变长度偏移阵列

我们来验证一下这个想法:

代码如下:

8f900a89c6347c561fdf2122f13be562.png View Code

91a223f96a77615cefb7dbb33660ab68.png

数据页内容

8f900a89c6347c561fdf2122f13be562.png View Code

15c748136f27683682806961275f5323.png

我们看第一行记录长度19怎麽得出来的

2个字节行标头存储了状态A和状态B的信息

2个字节存储固定长度大小

2个字节的列数

1个字节的null bitmap

4个字节存储int型数据

2个字节存储数据行中的可变长度列数量

4个字节存储可变长度偏移阵列  2*2=4

2个字节存储name列的值

2+2+2+1+4+2+4+2=19

也就是说,一行记录中全部的可变长度数据列的数据全部为NULL,才不会有这4个字节

2个字节存储数据行中的可变长度列数量

2个字节存储可变长度偏移阵列

其实SQLSERVER也做了一下标记,区分开一行记录中全部的可变长度类型列的数据全部为NULL还是一些为NULL一些不为NULL,还是全部不为NULL

这里可以在行记录属性中看出,testnullvarchar表的第一行和第二行

第一行NULL_BITMAP表明一行记录中全部的可变长度类型列的数据全部为NULL

第二行NULL_BITMAP VARIABLE_COLUMNS表明一些为NULL一些不为NULL或者全部不为NULL

939d4e77e34e32a658ff3e2004f520d4.png

小结:

VARCHAR类型NULL值不占用任何空间

testnotnullvarchar表

8f900a89c6347c561fdf2122f13be562.png View Code

数据页内容

8f900a89c6347c561fdf2122f13be562.png View Code

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 Slot 0 Offset 0x60 Length 11

2

3 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

4 Memory Dump @0x0855C060

5

6 00000000: 10000800 01000000 0200fc†††††††††††††...........

7

8 Slot 0 Column 0 Offset 0x4 Length 4

9

10 id = 1

11 NAME = [NULL]

12

13 Slot 1 Offset 0x6b Length 17

14

15 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS

16

17 Memory Dump @0x0855C06B

18

19 00000000: 30000800 02000000 0200fc01 001100c4 †0...............

20 00000010: e3†††††††††††††††††††††††††††††††††††.

21

22 Slot 1 Column 0 Offset 0x4 Length 4

23

24 id = 2

25

26 Slot 1 Column 1 Offset 0xf Length 2

27

28 NAME = 你

48304ba5e6f9fe08f3fa1abda7d326ab.png

3f8a46c32854117baed4d873e010a265.png

testnotnullvarchar表的数据页和testnullvarchar表的数据页对比一下

edac91fb4d81542a788599a5165ff1fe.png

看到上面的对比图我也不再对testnotnullvarchar表做详细分析了

情况跟testnullvarchar表是一样的

只有一个地方不一样,就是LEN()函数

3f8a46c32854117baed4d873e010a265.png

d266c9ac80093d64ce0ff7e36b680adb.png

小结:

对于varchar数据类型,无论是空字符串还是NULL值都不占用任何空间

CHAR类型的情况

testnullchar表

8f900a89c6347c561fdf2122f13be562.png View Code

数据页内容

8f900a89c6347c561fdf2122f13be562.png View Code

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 Slot 0 Offset 0x60 Length 31

2

3 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

4 Memory Dump @0x0A27C060

5

6 00000000: 10001c00 01000000 00000000 00000000 †................

7 00000010: 00000000 00000000 00000000 0200fe††††...............

8

9 Slot 0 Column 0 Offset 0x4 Length 4

10

11 id = 1

12 NAME = [NULL]

13

14 Slot 1 Offset 0x7f Length 31

15

16 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

17 Memory Dump @0x0A27C07F

18

19 00000000: 10001c00 02000000 c4e32020 20202020 †..........

20 00000010: 20202020 20202020 20202020 0200fc†††† ...

21

22 Slot 1 Column 0 Offset 0x4 Length 4

23

24 id = 2

25

26 Slot 1 Column 1 Offset 0x8 Length 20

27

28 NAME = 你

48304ba5e6f9fe08f3fa1abda7d326ab.png

58f357fa52a211e58a952f8738114d5e.png

6045bc064174f63ef378701fce3c1ea9.png

我们看第一行记录长度31怎麽得出来的

2个字节行标头存储了状态A和状态B的信息(2 bytes row header)

2个字节存储固定长度大小,因为一行记录了有varchar这些不固定长度的数据类型(2 bytes for length of fixed length columns)

4个字节存储int型数据(4 bytes for int (1st column))

2个字节的列数,用来存储这个表一共有多少列(2 bytes for number of columns in the table)

1个字节的null bitmap,(1 byte for null bitmap)

20个字节存储name列的值,为什麽用20个字节大家可以看一下

2+2+4+2+1+20=31

换言之,第一行记录中name字段是否为NULL,都占用20个字节的空间

d524034e45a2b2c39bd74144b1ddf2b2.png

-----------------------------------------------------------------------------------------

d04ac3aaecd825d526329dbb45c50b09.png

我们看第二行记录长度31怎麽得出来的

实际上第二行记录和第一行记录是一样的,只不过第二行记录里的name列存储了实际的值“你”,

而不管存储的值大小如何都占用20个字节

2个字节行标头存储了状态A和状态B的信息(2 bytes row header)

2个字节存储固定长度大小,因为一行记录了有varchar这些不固定长度的数据类型(2 bytes for length of fixed length columns)

4个字节存储int型数据(4 bytes for int (1st column))

2个字节的列数,用来存储这个表一共有多少列(2 bytes for number of columns in the table)

1个字节的null bitmap,(1 byte for null bitmap)

20个字节存储name列的值,为什麽用20个字节大家可以看一下

2+2+4+2+1+20=31

小结:

CHAR类型NULL值会占用空间,所占用空间大小取决于建表时候指定的char数据类型的大小

例如:

1 --允许空,char类型

2 CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL)

3 GO

指定char为20,那么就占用20个字节的空间

testnotnullchar表

8f900a89c6347c561fdf2122f13be562.png View Code

数据页内容

8f900a89c6347c561fdf2122f13be562.png View Code

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 Slot 0 Offset 0x60 Length 31

2

3 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

4 Memory Dump @0x0A01C060

5

6 00000000: 10001c00 01000000 20202020 20202020 †........

7 00000010: 20202020 20202020 20202020 0200fc†††† ...

8

9 Slot 0 Column 0 Offset 0x4 Length 4

10

11 id = 1

12

13 Slot 0 Column 1 Offset 0x8 Length 20

14

15 NAME =

16

17 Slot 1 Offset 0x7f Length 31

18

19 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP

20 Memory Dump @0x0A01C07F

21

22 00000000: 10001c00 02000000 c4e32020 20202020 †..........

23 00000010: 20202020 20202020 20202020 0200fc†††† ...

24

25 Slot 1 Column 0 Offset 0x4 Length 4

26

27 id = 2

28

29 Slot 1 Column 1 Offset 0x8 Length 20

30

31 NAME = 你

48304ba5e6f9fe08f3fa1abda7d326ab.png

4ab060f0dee1cb62afc03379871f7ea4.png

testnotnullchar表的数据页和testnullchar表的数据页对比一下

d590935627a989339f0fb14949a016c9.png

看到上面的对比图我也不再对testnotnullchar表做详细分析了

情况跟testnullchar表是一样的

只有两个地方不一样,testnotnullchar表的第一行记录的name字段存储的是空字符串

而testnullchar表的第一行记录的name字段存储的是NULL

2529b7091c5b439e84dee8ad43be6500.png

不过无论是空字符串还是NULL都占用了31个字节

LEN()函数返回的值不一样,这里跟varchar类型的情况也是一样的

58f357fa52a211e58a952f8738114d5e.png

4ab060f0dee1cb62afc03379871f7ea4.png

跟varchar类型不一样的是,testnotnullchar表的第一行记录的name字段存储的是空字符串,而testnullchar表的第一行记录的name字段存储的是NULL

varchar情况,testnotnullvarchar表和testnullvarchar表的第一行记录的name字段存储的都是NULL

bd1e0d335e090c5e92a8f3c69597d21f.png

而奇怪的是testnotnullvarchar表返回的不是NULL值,而是空字符串

91238eedf0d0d5001ff7b2ad6014c857.png

小结:

对于char数据类型,无论是空字符串还是NULL值都占用空间,所占用空间大小取决于建表时候指定的char数据类型的大小

例如:

1 --允许空,char类型

2 CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL)

3 GO

指定char为20,那么就占用20个字节的空间

总结

对于varchar数据类型,无论是空字符串还是NULL值都不占用任何空间

对于char数据类型,无论是空字符串还是NULL值都占用空间,所占用空间大小取决于建表时候指定的char数据类型的大小

从上面的实验来看,是否节省空间是根据数据类型来决定的而不是是否是NULL还是空字符串

撇开数据类型来比较是没有意义的,就像DATETIME数据类型的数据列填入NULL值和VARCHAR数据类型的数据列填入NULL值,

两个NULL值进行比较,哪一个大?如果不对两种数据类型进行分析,单独比较这两个NULL值,这种比较是没有意义的

而且也不平等,因为这两种数据类型一点关系都没有,一个datetime类型,一个是varchar类型

而char和varchar也是一样

只有同一种数据类型的比较才有意义,就像同样都是varchar数据类型,空字符串和NULL值进行比较

同样都是char数据类型,空字符串和NULL值进行比较

所以平时要对SQLSERVER中的数据类型要有一定认识,才能对系统中的表空间的使用情况有大概的掌握

如有不对的地方,欢迎大家拍砖o(∩_∩)o

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值