在今天的文章里,我想给你快速展现下当咱们从表里删除记录时,在SQL Server里发生了什么。首先咱们来建立一个简单的表,在8KB的页上恰好能插入4条记录。 sql
1 -- Create a simple table where 4 records fit onto 1 page 2 CREATE TABLE TestTable 3 ( 4 Col1 INT IDENTITY(1, 1), 5 Col2 CHAR(2000) 6 ) 7 GO
接下来咱们插入4条记录,这样的话一个页恰好所有填满。数组
1 -- Insert 4 records 2 INSERT INTO TestTable VALUES 3 ( 4 REPLICATE('1', 2000) 5 ), 6 ( 7 REPLICATE('2', 2000) 8 ), 9 ( 10 REPLICATE('3', 2000) 11 ), 12 ( 13 REPLICATE('4', 2000) 14 ) 15 GO
为了研究咱们堆表的细节,咱们使用DBCC PAGE命令来倾倒出分配的页面。所以咱们还要启用3604跟踪标志,这样的话SQL Server从DBCC PAGE命令直接把结果输入到咱们SSMS的会话窗口:spa
1 -- Enable the Trace Flag 3604 2 DBCC TRACEON(3604) 3 GO
咱们可使用DBCC IND命令返回全部分配给指定表或索引的页: code
1 -- Retrieve all pages of the table 2 DBCC IND(DataModifications, TestTable, -1) 3 GO
从输出能够看到,2个页属于咱们的表:数据页自己,还有IAM(索引分配图(index allocation map))页。blog
我这里的页号是118,经过DBCC PAGE命令倾倒出页面: 索引
1 -- Dump out one specific page 2 DBCC PAGE (DataModifications, 1, 118, 2) 3 GO
当你使用选项2的第3个参数倾倒,SQL Server返回你16进制的页倾倒,包括在页尾所谓的行偏移数组(Row Offset Array),不以任何方式影响数据。ci
行偏移数组指向在页上的物理位置,即每条记录存储的地方。第1条记录老是直接存储在页头偏移量96(0x60h)的地方。你也会看到,行偏移数组是逆向增加的。如今让咱们从表里删除第2条记录: rem
1 -- Delete a record from the table 2 DELETE FROM TestTable 3 WHERE Col1 = 2 4 GO
一般这里你会期待记录从页里删除。但事实上并不如此:当你再次执行DBCC PAGE命令时,你会看到在页上老记录的内容仍是能够看到。在DELETE操做期间,SQL Server惟一作的是,在页尾行偏移数组里,对应的槽无效了。get
如你所见,第2个槽的偏移量是0x0,这是无效的,意味着咱们的记录被删除了。在页开始部分,你总会找到96 bytes的页头。如今让咱们从表里删除其它的剩余3条记录。 it
1 -- Delete all the remaining records from the table 2 DELETE FROM TestTable 3 GO
当你再次用DBCC PAGE命令查看页,你会看到页所有内容仍是没改变:每条记录的每一个数据在页上仍是物理存在的!可是在行偏移数据里每条记录都指向偏移量0x0,这意味着每条记录都被删除。这与你的表是否使用了汇集索引无关——老数据在页上一直存在。
如今的问题是,SQL Server何时会初始化页?当你如今插入新的记录,SQL Server会覆盖页的原始内容。但在咱们的状况里,这只是物理部分,第1条记录存储的位置。你仍是能看到其它“删除”的记录内容。当你在页尾看下行偏移数组,你会看到它已被SQL Server彻底初始化了,也意味着你在行偏移数组里你如今只有1个槽了:
当你下次受权给程序sysadmin特权时,要考虑下这个状况了。使用合适的命令,这些程序仍是能看到已经“删除”的数据。
感谢关注!
https://www.sqlpassion.at/archive/2014/02/11/delete-operations-on-tables/