SQLSERVER NULL和空字符串的區別 使用NULL是否節省空間
這里只討論字符串類型,int、datetime、text這些數據類型就不討論了,因為是否節省空間是根據數據類型來定的
在寫這篇文章之前,本人一直以為這個問題很簡單的,看一下數據頁就行了,但是后來寫着寫着,也修改了幾次
發現需要對SQSERVER的數據頁內容很熟悉您才能知道SQLSERVER內部空間占用是怎樣的,希望大家在繼續往下看之前先看一下下面文章
在往下看之前請各位先看一下下面的文章
char nchar varchar nvarchar的區別 :char nchar varchar nvarchar數據類型所占用長度
SQL Server誤區30日談-Day6-有關NULL位圖的三個誤區
如果不看上面的文章,對於剛入門的人來說可能只會是一知半解,為了文章的篇幅不要過長,我就在文章里不解釋一些重要名詞了
大家看一下給出的文章就可以了o(∩_∩)o
先建立下面表格並插入測試數據

1 USE [pratice] 2 GO 3 4 5 --允許空,varchar類型 6 CREATE TABLE testnullvarchar(id INT ,NAME VARCHAR(20) NULL) 7 GO 8 --允許空,char類型 9 CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL) 10 GO 11 --不允許空,varchar類型 12 CREATE TABLE testnotnullvarchar(id INT ,NAME VARCHAR(20) NOT NULL) 13 GO 14 --不允許空,char類型 15 CREATE TABLE testnotnullchar(id INT ,NAME CHAR(20) NOT NULL) 16 GO 17 18 --插入數據 19 INSERT INTO [dbo].[testnullvarchar] ( [id],[Name] ) 20 SELECT 1 ,NULL UNION ALL 21 SELECT 2,'你' 22 GO 23 24 INSERT INTO [dbo].[testnullchar] ( [id],[Name] ) 25 SELECT 1,NULL UNION ALL 26 SELECT 2,'你' 27 GO 28 29 INSERT INTO [dbo].[testnotnullchar] ( [id],[NAME] ) 30 SELECT 1,'' UNION ALL 31 SELECT 2,'你' 32 GO 33 34 INSERT INTO [dbo].[testnotnullvarchar] ( [id],[NAME] ) 35 SELECT 1,'' UNION ALL 36 SELECT 2,'你' 37 GO 38 39 SELECT * FROM testnullvarchar 40 SELECT * FROM testnullchar 41 SELECT * FROM testnotnullchar 42 SELECT * FROM testnotnullvarchar
建立一個DBCCResult表,保存DBCC IND的結果

1 CREATE TABLE DBCCResult ( 2 PageFID NVARCHAR(200), 3 PagePID NVARCHAR(200), 4 IAMFID NVARCHAR(200), 5 IAMPID NVARCHAR(200), 6 ObjectID NVARCHAR(200), 7 IndexID NVARCHAR(200), 8 PartitionNumber NVARCHAR(200), 9 PartitionID NVARCHAR(200), 10 iam_chain_type NVARCHAR(200), 11 PageType NVARCHAR(200), 12 IndexLevel NVARCHAR(200), 13 NextPageFID NVARCHAR(200), 14 NextPagePID NVARCHAR(200), 15 PrevPageFID NVARCHAR(200), 16 PrevPagePID NVARCHAR(200) 17 ) 18 GO
查看各張表的情況
VARCHAR類型的情況
testnullvarchar表

1 --TRUNCATE TABLE DBCCResult 2 INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullvarchar,-1) ') 3 4 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 5 6 7 DBCC TRACEON(3604,-1) 8 GO 9 DBCC PAGE([pratice],1,8370,3) 10 GO 11 12 13 14 SELECT LEN(name) FROM testnullvarchar WHERE [id]=1
數據頁內容

1 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。 2 3 PAGE: (1:8370) 4 5 6 BUFFER: 7 8 9 BUF @0x03CF4E64 10 11 bpage = 0x16F16000 bhash = 0x00000000 bpageno = (1:8370) 12 bdbid = 5 breferences = 0 bUse1 = 9390 13 bstat = 0x2c0000b blog = 0x32159bb bnext = 0x00000000 14 15 PAGE HEADER: 16 17 18 Page @0x16F16000 19 20 m_pageId = (1:8370) m_headerVersion = 1 m_type = 1 21 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000 22 m_objId (AllocUnitId.idObj) = 521 m_indexId (AllocUnitId.idInd) = 256 23 Metadata: AllocUnitId = 72057594072072192 24 Metadata: PartitionId = 72057594059882496 Metadata: IndexId = 0 25 Metadata: ObjectId = 1207675350 m_prevPage = (0:0) m_nextPage = (0:0) 26 pminlen = 8 m_slotCnt = 2 m_freeCnt = 8064 27 m_freeData = 124 m_reservedCnt = 0 m_lsn = (3045:22651:20) 28 m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 29 m_tornBits = 0 30 31 Allocation Status 32 33 GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED 34 PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED 35 ML (1:7) = NOT MIN_LOGGED 36 37 Slot 0 Offset 0x60 Length 11 38 39 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 40 Memory Dump @0x0A16C060 41 42 00000000: 10000800 01000000 0200fe†††††††††††††........... 43 44 Slot 0 Column 0 Offset 0x4 Length 4 45 46 id = 1 47 NAME = [NULL] 48 49 Slot 1 Offset 0x6b Length 17 50 51 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 52 53 Memory Dump @0x0A16C06B 54 55 00000000: 30000800 02000000 0200fc01 001100c4 †0............... 56 00000010: e3†††††††††††††††††††††††††††††††††††. 57 58 Slot 1 Column 0 Offset 0x4 Length 4 59 60 id = 2 61 62 Slot 1 Column 1 Offset 0xf Length 2 63 64 NAME = 你 65 66 67 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
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 = 你
我們看第一行記錄長度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個字節存儲可變長度偏移陣列
我們來驗證一下這個想法:
代碼如下:

1 USE [pratice] 2 GO 3 -- 4 CREATE TABLE testnullandnotnullvarchar(id INT ,NAME1 VARCHAR(20) NULL,NAME2 VARCHAR(20) NULL) 5 GO 6 7 --插入數據 8 INSERT INTO [dbo].[testnullandnotnullvarchar] ( [id],[Name1],[NAME2] ) 9 SELECT 1 ,NULL,'你' 10 GO 11 12 SELECT * FROM testnullandnotnullvarchar 13 ----------------------------------------------------------- 14 --TRUNCATE TABLE DBCCResult 15 INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullandnotnullvarchar,-1) ') 16 17 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 18 19 20 DBCC TRACEON(3604,-1) 21 GO 22 DBCC PAGE([pratice],1,15656,3) 23 GO
數據頁內容

1 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。 2 3 PAGE: (1:15656) 4 5 6 BUFFER: 7 8 9 BUF @0x03D42838 10 11 bpage = 0x196A6000 bhash = 0x00000000 bpageno = (1:15656) 12 bdbid = 5 breferences = 0 bUse1 = 32718 13 bstat = 0xc0000b blog = 0xbbbbbbbb bnext = 0x00000000 14 15 PAGE HEADER: 16 17 18 Page @0x196A6000 19 20 m_pageId = (1:15656) m_headerVersion = 1 m_type = 1 21 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000 22 m_objId (AllocUnitId.idObj) = 531 m_indexId (AllocUnitId.idInd) = 256 23 Metadata: AllocUnitId = 72057594072727552 24 Metadata: PartitionId = 72057594060537856 Metadata: IndexId = 0 25 Metadata: ObjectId = 1383675977 m_prevPage = (0:0) m_nextPage = (0:0) 26 pminlen = 8 m_slotCnt = 1 m_freeCnt = 8075 27 m_freeData = 115 m_reservedCnt = 0 m_lsn = (3045:22996:18) 28 m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 29 m_tornBits = 0 30 31 Allocation Status 32 33 GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED 34 PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED 35 ML (1:7) = NOT MIN_LOGGED 36 37 Slot 0 Offset 0x60 Length 19 38 39 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 40 41 Memory Dump @0x0855C060 42 43 00000000: 30000800 01000000 0300fa02 00110013 †0............... 44 00000010: 00c4e3†††††††††††††††††††††††††††††††... 45 46 Slot 0 Column 0 Offset 0x4 Length 4 47 48 id = 1 49 NAME1 = [NULL] 50 51 Slot 0 Column 2 Offset 0x11 Length 2 52 53 NAME2 = 你 54 55 56 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表

1 --TRUNCATE TABLE DBCCResult 2 INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnotnullvarchar,-1) ') 3 4 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 5 6 7 DBCC TRACEON(3604,-1) 8 GO 9 DBCC PAGE([pratice],1,14495,3) 10 GO 11 12 13 SELECT LEN(name) FROM testnotnullvarchar WHERE [id]=1
數據頁內容

1 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。 2 3 PAGE: (1:14495) 4 5 6 BUFFER: 7 8 9 BUF @0x03D4258C 10 11 bpage = 0x196D2000 bhash = 0x00000000 bpageno = (1:14495) 12 bdbid = 5 breferences = 0 bUse1 = 38447 13 bstat = 0xc0000b blog = 0xbbbbbbbb bnext = 0x00000000 14 15 PAGE HEADER: 16 17 18 Page @0x196D2000 19 20 m_pageId = (1:14495) m_headerVersion = 1 m_type = 1 21 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000 22 m_objId (AllocUnitId.idObj) = 532 m_indexId (AllocUnitId.idInd) = 256 23 Metadata: AllocUnitId = 72057594072793088 24 Metadata: PartitionId = 72057594060603392 Metadata: IndexId = 0 25 Metadata: ObjectId = 1399676034 m_prevPage = (0:0) m_nextPage = (0:0) 26 pminlen = 8 m_slotCnt = 2 m_freeCnt = 8064 27 m_freeData = 124 m_reservedCnt = 0 m_lsn = (3045:23036:20) 28 m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 29 m_tornBits = 0 30 31 Allocation Status 32 33 GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED 34 PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED 35 ML (1:7) = NOT MIN_LOGGED 36 37 Slot 0 Offset 0x60 Length 11 38 39 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 40 Memory Dump @0x0855C060 41 42 00000000: 10000800 01000000 0200fc†††††††††††††........... 43 44 Slot 0 Column 0 Offset 0x4 Length 4 45 46 id = 1 47 NAME = [NULL] 48 49 Slot 1 Offset 0x6b Length 17 50 51 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 52 53 Memory Dump @0x0855C06B 54 55 00000000: 30000800 02000000 0200fc01 001100c4 †0............... 56 00000010: e3†††††††††††††††††††††††††††††††††††. 57 58 Slot 1 Column 0 Offset 0x4 Length 4 59 60 id = 2 61 62 Slot 1 Column 1 Offset 0xf Length 2 63 64 NAME = 你 65 66 67 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
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 = 你
testnotnullvarchar表的數據頁和testnullvarchar表的數據頁對比一下
看到上面的對比圖我也不再對testnotnullvarchar表做詳細分析了
情況跟testnullvarchar表是一樣的
只有一個地方不一樣,就是LEN()函數
小結:
對於varchar數據類型,無論是空字符串還是NULL值都不占用任何空間
CHAR類型的情況
testnullchar表

1 --TRUNCATE TABLE DBCCResult 2 INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnullchar,-1) ') 3 4 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 5 6 7 DBCC TRACEON(3604,-1) 8 GO 9 DBCC PAGE([pratice],1,8353,3) 10 GO 11 12 13 14 SELECT LEN(name) FROM testnullchar WHERE [id]=1
數據頁內容

1 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。 2 3 PAGE: (1:8353) 4 5 6 BUFFER: 7 8 9 BUF @0x03CF68D0 10 11 bpage = 0x16FA8000 bhash = 0x00000000 bpageno = (1:8353) 12 bdbid = 5 breferences = 0 bUse1 = 39861 13 bstat = 0x2c0000b blog = 0x59bbbbbb bnext = 0x00000000 14 15 PAGE HEADER: 16 17 18 Page @0x16FA8000 19 20 m_pageId = (1:8353) m_headerVersion = 1 m_type = 1 21 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000 22 m_objId (AllocUnitId.idObj) = 533 m_indexId (AllocUnitId.idInd) = 256 23 Metadata: AllocUnitId = 72057594072858624 24 Metadata: PartitionId = 72057594060668928 Metadata: IndexId = 0 25 Metadata: ObjectId = 1415676091 m_prevPage = (0:0) m_nextPage = (0:0) 26 pminlen = 28 m_slotCnt = 2 m_freeCnt = 8030 27 m_freeData = 158 m_reservedCnt = 0 m_lsn = (3045:23074:20) 28 m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 29 m_tornBits = 0 30 31 Allocation Status 32 33 GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED 34 PFS (1:8088) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED 35 ML (1:7) = NOT MIN_LOGGED 36 37 Slot 0 Offset 0x60 Length 31 38 39 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 40 Memory Dump @0x0A27C060 41 42 00000000: 10001c00 01000000 00000000 00000000 †................ 43 00000010: 00000000 00000000 00000000 0200fe††††............... 44 45 Slot 0 Column 0 Offset 0x4 Length 4 46 47 id = 1 48 NAME = [NULL] 49 50 Slot 1 Offset 0x7f Length 31 51 52 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 53 Memory Dump @0x0A27C07F 54 55 00000000: 10001c00 02000000 c4e32020 20202020 †.......... 56 00000010: 20202020 20202020 20202020 0200fc†††† ... 57 58 Slot 1 Column 0 Offset 0x4 Length 4 59 60 id = 2 61 62 Slot 1 Column 1 Offset 0x8 Length 20 63 64 NAME = 你 65 66 67 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
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 = 你
我們看第一行記錄長度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數據類型的大小
例如:
1 --允許空,char類型 2 CREATE TABLE testnullchar(id INT,NAME CHAR(20) NULL) 3 GO
指定char為20,那么就占用20個字節的空間
testnotnullchar表

1 --TRUNCATE TABLE DBCCResult 2 INSERT INTO DBCCResult EXEC ('DBCC IND(pratice,testnotnullchar,-1) ') 3 4 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 5 6 7 DBCC TRACEON(3604,-1) 8 GO 9 DBCC PAGE([pratice],1,37266,3) 10 GO 11 12 13 14 SELECT LEN(name) FROM testnotnullchar WHERE [id]=1
數據頁內容

1 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。 2 3 PAGE: (1:37266) 4 5 6 BUFFER: 7 8 9 BUF @0x03D669F0 10 11 bpage = 0x1A500000 bhash = 0x00000000 bpageno = (1:37266) 12 bdbid = 5 breferences = 0 bUse1 = 42107 13 bstat = 0xc0000b blog = 0x9bbbbbbb bnext = 0x00000000 14 15 PAGE HEADER: 16 17 18 Page @0x1A500000 19 20 m_pageId = (1:37266) m_headerVersion = 1 m_type = 1 21 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000 22 m_objId (AllocUnitId.idObj) = 534 m_indexId (AllocUnitId.idInd) = 256 23 Metadata: AllocUnitId = 72057594072924160 24 Metadata: PartitionId = 72057594060734464 Metadata: IndexId = 0 25 Metadata: ObjectId = 1431676148 m_prevPage = (0:0) m_nextPage = (0:0) 26 pminlen = 28 m_slotCnt = 2 m_freeCnt = 8030 27 m_freeData = 158 m_reservedCnt = 0 m_lsn = (3045:23137:20) 28 m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 29 m_tornBits = 0 30 31 Allocation Status 32 33 GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED 34 PFS (1:32352) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED 35 ML (1:7) = NOT MIN_LOGGED 36 37 Slot 0 Offset 0x60 Length 31 38 39 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 40 Memory Dump @0x0A01C060 41 42 00000000: 10001c00 01000000 20202020 20202020 †........ 43 00000010: 20202020 20202020 20202020 0200fc†††† ... 44 45 Slot 0 Column 0 Offset 0x4 Length 4 46 47 id = 1 48 49 Slot 0 Column 1 Offset 0x8 Length 20 50 51 NAME = 52 53 Slot 1 Offset 0x7f Length 31 54 55 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP 56 Memory Dump @0x0A01C07F 57 58 00000000: 10001c00 02000000 c4e32020 20202020 †.......... 59 00000010: 20202020 20202020 20202020 0200fc†††† ... 60 61 Slot 1 Column 0 Offset 0x4 Length 4 62 63 id = 2 64 65 Slot 1 Column 1 Offset 0x8 Length 20 66 67 NAME = 你 68 69 70 DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
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 = 你
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數據類型的大小
例如:
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