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

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

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

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

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

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

SQL Server页中行物理存储

SQL Server误区30日谈-Day6-有关NULL位图的三个误区

SQL Server2008存储结构之堆表、行溢出

SQLSERVER中NULL位图的作用

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

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


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

 USE [pratice]
GO --允许空,varchar类型
CREATE TABLE testnullvarchar(id INT ,NAME VARCHAR(20) NULL)
GO
--允许空,char类型
CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL)
GO
--不允许空,varchar类型
CREATE TABLE testnotnullvarchar(id INT ,NAME VARCHAR(20) NOT NULL)
GO
--不允许空,char类型
CREATE TABLE testnotnullchar(id INT ,NAME CHAR(20) NOT NULL)
GO --插入数据
INSERT INTO [dbo].[testnullvarchar] ( [id],[Name] )
SELECT 1 ,NULL UNION ALL
SELECT 2,'你'
GO INSERT INTO [dbo].[testnullchar] ( [id],[Name] )
SELECT 1,NULL UNION ALL
SELECT 2,'你'
GO INSERT INTO [dbo].[testnotnullchar] ( [id],[NAME] )
SELECT 1,'' UNION ALL
SELECT 2,'你'
GO INSERT INTO [dbo].[testnotnullvarchar] ( [id],[NAME] )
SELECT 1,'' UNION ALL
SELECT 2,'你'
GO SELECT * FROM testnullvarchar
SELECT * FROM testnullchar
SELECT * FROM testnotnullchar
SELECT * FROM testnotnullvarchar

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

 CREATE TABLE DBCCResult (
PageFID NVARCHAR(200),
PagePID NVARCHAR(200),
IAMFID NVARCHAR(200),
IAMPID NVARCHAR(200),
ObjectID NVARCHAR(200),
IndexID NVARCHAR(200),
PartitionNumber NVARCHAR(200),
PartitionID NVARCHAR(200),
iam_chain_type NVARCHAR(200),
PageType NVARCHAR(200),
IndexLevel NVARCHAR(200),
NextPageFID NVARCHAR(200),
NextPagePID NVARCHAR(200),
PrevPageFID NVARCHAR(200),
PrevPagePID NVARCHAR(200)
)
GO

查看各张表的情况

VARCHAR类型的情况

testnullvarchar表

 --TRUNCATE TABLE DBCCResult
INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullvarchar,-1) ') SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC DBCC TRACEON(3604,-1)
GO
DBCC PAGE([pratice],1,8370,3)
GO SELECT LEN(name) FROM testnullvarchar WHERE [id]=1

数据页内容

 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

 PAGE: (1:8370)

 BUFFER:

 BUF @0x03CF4E64

 bpage = 0x16F16000                   bhash = 0x00000000                   bpageno = (1:8370)
bdbid = 5 breferences = 0 bUse1 = 9390
bstat = 0x2c0000b blog = 0x32159bb bnext = 0x00000000 PAGE HEADER: Page @0x16F16000 m_pageId = (1:8370) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 521 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594072072192
Metadata: PartitionId = 72057594059882496 Metadata: IndexId = 0
Metadata: ObjectId = 1207675350 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 8 m_slotCnt = 2 m_freeCnt = 8064
m_freeData = 124 m_reservedCnt = 0 m_lsn = (3045:22651:20)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0 Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED Slot 0 Offset 0x60 Length 11 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A16C060 00000000: 10000800 01000000 0200fe†††††††††††††........... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x6b Length 17 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Memory Dump @0x0A16C06B 00000000: 30000800 02000000 0200fc01 001100c4 †0...............
00000010: e3†††††††††††††††††††††††††††††††††††. Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0xf Length 2 NAME = 你 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
 Slot 0 Offset 0x60 Length 11

 Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x0A16C060 00000000: 10000800 01000000 0200fe†††††††††††††........... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x6b Length 17 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Memory Dump @0x0A16C06B 00000000: 30000800 02000000 0200fc01 001100c4 †0...............
00000010: e3†††††††††††††††††††††††††††††††††††. Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0xf Length 2 NAME = 你

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

SQL Server页中行物理存储里对数据行的各段进行了解释

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

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

我们看第二行记录长度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个字节存储可变长度偏移阵列,可变长度偏移阵列的公式

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

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

2个字节存储name列的值,为什麽用两个字节大家可以看一下char nchar varchar nvarchar的区别 2 bytes for name (你)

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

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

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

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

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

想法:

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

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

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

代码如下:

 USE [pratice]
GO
--
CREATE TABLE testnullandnotnullvarchar(id INT ,NAME1 VARCHAR(20) NULL,NAME2 VARCHAR(20) NULL)
GO --插入数据
INSERT INTO [dbo].[testnullandnotnullvarchar] ( [id],[Name1],[NAME2] )
SELECT 1 ,NULL,'你'
GO SELECT * FROM testnullandnotnullvarchar
-----------------------------------------------------------
--TRUNCATE TABLE DBCCResult
INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullandnotnullvarchar,-1) ') SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC DBCC TRACEON(3604,-1)
GO
DBCC PAGE([pratice],1,15656,3)
GO

数据页内容

 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

 PAGE: (1:15656)

 BUFFER:

 BUF @0x03D42838

 bpage = 0x196A6000                   bhash = 0x00000000                   bpageno = (1:15656)
bdbid = 5 breferences = 0 bUse1 = 32718
bstat = 0xc0000b blog = 0xbbbbbbbb bnext = 0x00000000 PAGE HEADER: Page @0x196A6000 m_pageId = (1:15656) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 531 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594072727552
Metadata: PartitionId = 72057594060537856 Metadata: IndexId = 0
Metadata: ObjectId = 1383675977 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 8 m_slotCnt = 1 m_freeCnt = 8075
m_freeData = 115 m_reservedCnt = 0 m_lsn = (3045:22996:18)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0 Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED Slot 0 Offset 0x60 Length 19 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Memory Dump @0x0855C060 00000000: 30000800 01000000 0300fa02 00110013 †0...............
00000010: 00c4e3†††††††††††††††††††††††††††††††... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME1 = [NULL] Slot 0 Column 2 Offset 0x11 Length 2 NAME2 = 你 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

我们看第一行记录长度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

小结:

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

testnotnullvarchar表

 --TRUNCATE TABLE DBCCResult
INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnotnullvarchar,-1) ') SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC DBCC TRACEON(3604,-1)
GO
DBCC PAGE([pratice],1,14495,3)
GO SELECT LEN(name) FROM testnotnullvarchar WHERE [id]=1

数据页内容

 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

 PAGE: (1:14495)

 BUFFER:

 BUF @0x03D4258C

 bpage = 0x196D2000                   bhash = 0x00000000                   bpageno = (1:14495)
bdbid = 5 breferences = 0 bUse1 = 38447
bstat = 0xc0000b blog = 0xbbbbbbbb bnext = 0x00000000 PAGE HEADER: Page @0x196D2000 m_pageId = (1:14495) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 532 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594072793088
Metadata: PartitionId = 72057594060603392 Metadata: IndexId = 0
Metadata: ObjectId = 1399676034 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 8 m_slotCnt = 2 m_freeCnt = 8064
m_freeData = 124 m_reservedCnt = 0 m_lsn = (3045:23036:20)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0 Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED Slot 0 Offset 0x60 Length 11 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0855C060 00000000: 10000800 01000000 0200fc†††††††††††††........... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x6b Length 17 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Memory Dump @0x0855C06B 00000000: 30000800 02000000 0200fc01 001100c4 †0...............
00000010: e3†††††††††††††††††††††††††††††††††††. Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0xf Length 2 NAME = 你 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
 Slot 0 Offset 0x60 Length 11

 Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x0855C060 00000000: 10000800 01000000 0200fc†††††††††††††........... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x6b Length 17 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Memory Dump @0x0855C06B 00000000: 30000800 02000000 0200fc01 001100c4 †0...............
00000010: e3†††††††††††††††††††††††††††††††††††. Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0xf Length 2 NAME = 你

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

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

情况跟testnullvarchar表是一样的

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

小结:

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


CHAR类型的情况

testnullchar表

 --TRUNCATE TABLE DBCCResult
INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullchar,-1) ') SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC DBCC TRACEON(3604,-1)
GO
DBCC PAGE([pratice],1,8353,3)
GO SELECT LEN(name) FROM testnullchar WHERE [id]=1

数据页内容

 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

 PAGE: (1:8353)

 BUFFER:

 BUF @0x03CF68D0

 bpage = 0x16FA8000                   bhash = 0x00000000                   bpageno = (1:8353)
bdbid = 5 breferences = 0 bUse1 = 39861
bstat = 0x2c0000b blog = 0x59bbbbbb bnext = 0x00000000 PAGE HEADER: Page @0x16FA8000 m_pageId = (1:8353) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 533 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594072858624
Metadata: PartitionId = 72057594060668928 Metadata: IndexId = 0
Metadata: ObjectId = 1415676091 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 28 m_slotCnt = 2 m_freeCnt = 8030
m_freeData = 158 m_reservedCnt = 0 m_lsn = (3045:23074:20)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0 Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED Slot 0 Offset 0x60 Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A27C060 00000000: 10001c00 01000000 00000000 00000000 †................
00000010: 00000000 00000000 00000000 0200fe††††............... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x7f Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A27C07F 00000000: 10001c00 02000000 c4e32020 20202020 †..........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0x8 Length 20 NAME = 你 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
 Slot 0 Offset 0x60 Length 31

 Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x0A27C060 00000000: 10001c00 01000000 00000000 00000000 †................
00000010: 00000000 00000000 00000000 0200fe††††............... Slot 0 Column 0 Offset 0x4 Length 4 id = 1
NAME = [NULL] Slot 1 Offset 0x7f Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A27C07F 00000000: 10001c00 02000000 c4e32020 20202020 †..........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0x8 Length 20 NAME = 你

我们看第一行记录长度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个字节大家可以看一下char nchar varchar nvarchar的区别  20 bytes for char(20) (2nd column)

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

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

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

我们看第二行记录长度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个字节大家可以看一下char nchar varchar nvarchar的区别 20 bytes for char(20) (2nd column)

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

小结:

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

例如:

 --允许空,char类型
CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL)
GO

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

testnotnullchar表

 --TRUNCATE TABLE DBCCResult
INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnotnullchar,-1) ') SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC DBCC TRACEON(3604,-1)
GO
DBCC PAGE([pratice],1,37266,3)
GO SELECT LEN(name) FROM testnotnullchar WHERE [id]=1

数据页内容

 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

 PAGE: (1:37266)

 BUFFER:

 BUF @0x03D669F0

 bpage = 0x1A500000                   bhash = 0x00000000                   bpageno = (1:37266)
bdbid = 5 breferences = 0 bUse1 = 42107
bstat = 0xc0000b blog = 0x9bbbbbbb bnext = 0x00000000 PAGE HEADER: Page @0x1A500000 m_pageId = (1:37266) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 534 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594072924160
Metadata: PartitionId = 72057594060734464 Metadata: IndexId = 0
Metadata: ObjectId = 1431676148 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 28 m_slotCnt = 2 m_freeCnt = 8030
m_freeData = 158 m_reservedCnt = 0 m_lsn = (3045:23137:20)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0 Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:32352) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED Slot 0 Offset 0x60 Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A01C060 00000000: 10001c00 01000000 20202020 20202020 †........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 0 Column 0 Offset 0x4 Length 4 id = 1 Slot 0 Column 1 Offset 0x8 Length 20 NAME = Slot 1 Offset 0x7f Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A01C07F 00000000: 10001c00 02000000 c4e32020 20202020 †..........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0x8 Length 20 NAME = 你 DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
 Slot 0 Offset 0x60 Length 31

 Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x0A01C060 00000000: 10001c00 01000000 20202020 20202020 †........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 0 Column 0 Offset 0x4 Length 4 id = 1 Slot 0 Column 1 Offset 0x8 Length 20 NAME = Slot 1 Offset 0x7f Length 31 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x0A01C07F 00000000: 10001c00 02000000 c4e32020 20202020 †..........
00000010: 20202020 20202020 20202020 0200fc†††† ... Slot 1 Column 0 Offset 0x4 Length 4 id = 2 Slot 1 Column 1 Offset 0x8 Length 20 NAME = 你

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

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

情况跟testnullchar表是一样的

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

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

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

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

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

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

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

小结:

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

例如:

 --允许空,char类型
CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL)
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

SQLSERVER NULL和空字符串的区别 使用NULL是否节省空间的更多相关文章

  1. Oracle中Null与空字符串' '的区别

    含义解释: 问:什么是NULL? 答:在我们不知道具体有什么数据的时候,也即未知,可以用NULL,我们称它为空,ORACLE中,含有空值的表列长度为零. ORACLE允许任何一种数据类型的字段为空,除 ...

  2. Django与SQL语言中——NULL与空字符串的区别

    SQL有指定空值的独特方式,它把空值叫做NULL. Null在数据库中表示 不知道的数据,主要有3种意思: 1)知道数据存在,但不知道具体值. 2)不知道数据是否存在. 3)数据不存在. 在SQL中, ...

  3. Oracle 与 Mysql NULL值,空字符串''的区别

    Oracle(null等同于空字符'') 1.oracle插入空字符串默认替换成null 2.oracle查询(null和被替换的空字符)时使用 is null/is not null 3.使用聚合函 ...

  4. request.getParameter("name")获取参数为null和空字符串的区别

    1.获取到的值为空字符串 当url里有name属性,但是没有值的时候,后台用request.getParameter("name")获取到的是空字符串 2.获取到的值为null 当 ...

  5. Oracle中的null与空字符串''的区别

    含义解释:问:什么是NULL?答:在我们不知道具体有什么数据的时候,也即未知,可以用NULL,我们称它为空,ORACLE中,含有空值的表列长度为零.ORACLE允许任何一种数据类型的字段为空,除了以下 ...

  6. sql中null 和 ‘’(空字符串)

    sql 中 null  和 空字符串的区别方式 在Silverlight中  数据库 需要与实体类进行映射, 假如实体类不允许为null,则 select '' as 列名  from  表名字:   ...

  7. 关于StringUtils类isEmpty、isNotEmpty、isBlank、isNotBlank针对null、空字符串和空白字符(如空格、制表符)的区别

    isEmpty | null | 空字符串("")|空白字符(空格.制表符)| | isEmpty | true | true | false | | isNotEmpty | f ...

  8. Java知识点-判断null、空字符串和空格

    Java知识点-判断null.空字符串和空格 // 判断headerKey是否为null,空字符串或者空格 if (headerKey != null && headerKey.len ...

  9. MySQL中NULL与空字符串

    一些刚刚接触MySQL的孩子,经常会错误的认为NULL与空字符串’  ’是相同的.这看似是一件不重要的事情,但是在MySQL中,这两者是完全不同的.NULL是指没有值,而”则表示值是存在的,只不过是个 ...

随机推荐

  1. WPF 中textBox实现只输入数字

    刚学到 通过本方法可以使文本框只能输入或复制入数字  对于数量类输入文本框比较有用 金额类只需小改动也可实现 以TextBox txtCount为例 添加TextChanged事件 代码如下 priv ...

  2. Vue + Element UI 实现权限管理系统 前端篇(一):搭建开发环境

    技术基础 开发之前,请先熟悉下面的4个文档 vue.js2.0中文, 优秀的JS框架 vue-router, vue.js 配套路由 vuex,vue.js 应用状态管理库 Element,饿了么提供 ...

  3. 基于HA机制的MyCat架构——配置HAProxy

    HAProxy简介HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案. HAProxy特别适用于那些负载特大的web站点,这些站 ...

  4. MySql主从同步和延迟同步

    MySql同步与延迟同步 Mysql同步 一 Mysql主服务器上操作 1 开启服务器上的log_bin功能 # vim/etc/my.cnf 增加一下两行 log_bin=mysql-bin ser ...

  5. 浅析Session和Cookie

    Cookie   Cookie的作用,就是当一个用户通过http访问一个服务器时,这个服务器会将一些key/value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户访问该 ...

  6. python 常用算法学习(1)

    算法就是为了解决某一个问题而采取的具体有效的操作步骤 算法的复杂度,表示代码的运行效率,用一个大写的O加括号来表示,比如O(1),O(n) 认为算法的复杂度是渐进的,即对于一个大小为n的输入,如果他的 ...

  7. AD阶段分类论文阅读笔记

    A Deep Learning Pipeline for Classifying Different Stages of Alzheimer's Disease from fMRI Data -- Y ...

  8. python的Web框架:Django路由系统以及模板导入

    Django的路由系统 当一个请求来到时 当一个请求来到时 1.首先到项目目录下的urls.py(根URLconf模块)中,查找路由规则: 2.根URELcof模块,里面定义了 urlpatterns ...

  9. DataGridView列标题(列标头)不能居中的解决方法

    winform DataGridView列标题(列标头)不能完全居中的解决方法,一般列标题的居中我们都使用 DgvDemo.ColumnHeadersDefaultCellStyle.Alignmen ...

  10. angularjs学习第三天笔记(过滤器第二篇---filter过滤器及其自定义过滤器)

    您好,我是一名后端开发工程师,由于工作需要,现在系统的从0开始学习前端js框架之angular,每天把学习的一些心得分享出来,如果有什么说的不对的地方,请多多指正,多多包涵我这个前端菜鸟,欢迎大家的点 ...