您的位置:首页 > 其它

update操作是先delete后insert的吗?

2011-12-29 10:02 288 查看
最近较忙,很少出炉技术文章了。但最近听得很多的一句话就是update操作实际上就是先delete后insert的操作。对于这种提法,网上也比较普遍,但本人确不敢苟同。我们现在通过下面的几个case来看看究竟是不是那样的。

我们会分3种情形来测试:
1、更改非聚集索引键列字段,更改前后的长度一样;
2、更改非聚集索引键列字段,更改后的长度比之前的大;
3、更改聚集索引键列。

下面先来创建两个表:

create table t1
(
id int,
col varchar(10)
)
create unique clustered index IXCU_T1_ID on t1(id)
go
create table t2
(
id int,
col varchar(10)
)
create unique clustered index IXCU_T2_ID on t2(id)
go

插入一些测试数据

insert into t1 select 1,'aa'
insert into t1 select 2,'bb'
insert into t1 select 3,'cc'
go
insert into t2 select 1,'aa'
insert into t2 select 2,'bb'
insert into t2 select 3,'cc'
go

查看所在的数据库的ID号以及两个表对应的ID:

select db_id(),object_id('t1'),object_id('t2')

查看这2个表使用了那些扩展盘区和数据页

dbcc extentinfo(5,789577851)--这里5是刚刚查出来的数据库的ID,789577851是表t1的ID
dbcc extentinfo(5,805577908)--这里5是刚刚查出来的数据库的ID,805577908是表t2的ID

查看2个表所在页面上每条记录的存储情况:

dbcc traceon(3604)
dbcc page(5,1,152432,1)--这里5是刚刚查出来的数据库的ID,1是运行dbcc extentinfo查出来的字段file_id的值,152432是字段page_id的值,最后这个1是参数,可在0、1、2、3中任选。
dbcc page(5,1,201088,1)

语句执行后我们得到下面的结果:

表t1的:

Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 113 (0x71)
0 (0x0) - 96 (0x60)

表t2的:

Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 113 (0x71)
0 (0x0) - 96 (0x60)

可以看到2个表的记录在数据页上的存储是一模一样的。

现在我们来测试第一种情形:更改非聚集索引键列字段,更改前后的长度一样

对于表t1,执行下面的操作:

update t1 set col='dd' where id=2

对于表t2执行下面的操作:

delete from t2 where id=2
insert into t2 select 2,'dd'

然后在运行下面的命令查看页面数据的存储情况:

dbcc page(5,1,152432,1)
dbcc page(5,1,201088,1)

这是表t1的查询结果:

Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 113 (0x71)
0 (0x0) - 96 (0x60)

这是表t2的查询结果

Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 147 (0x93)
0 (0x0) - 96 (0x60)

可以看到此时数据的存储不一样了,表t1的数据存储没有变化。表t2的存储发生变化了,原先在113和130之间存储的是第二条记录,现在这条记录却存储到了147个字节以后了,而原来113和130之间什么也没存储,这样这里就形成了内部碎片。对于这种update后数据的存储位置不发生变化的更新称为现场更新,如果位置发生了改变就称为非现在更新。

所以对于这种情形来说:update操作并不是先delete后insert的。

下面我们来测试第二种情形:即更改非聚集索引键列字段,更改后的长度比之前的大(比之前小的情形,大家自行测试吧)。

对于表t1,执行下面的语句:

update t1 set col='aaa' where id=2

查看数据页上的存储情况:

dbcc page(5,1,152432,1)

结果如下:
Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 147 (0x93)
0 (0x0) - 96 (0x60)

这时我们看到它跟先delete后insert时的情形一模一样了。
所以对于这种情形而言:update操作相当于先delete后insert。

下面我们测试第三种情形:更改聚集索引键列。

先来把刚才测试的表删除,再重新创建一个干净的测试环境:

drop table t1,t2

create table t1
(
id int,
col varchar(10)
)
create unique clustered index IXCU_T1_ID on t1(id)
go
create table t2
(
id int,
col varchar(10)
)
create unique clustered index IXCU_T2_ID on t2(id)
go
insert into t1 select 1,'aa'
insert into t1 select 2,'bb'
insert into t1 select 3,'cc'
go
insert into t2 select 1,'aa'
insert into t2 select 2,'bb'
insert into t2 select 3,'cc'
go

对表t1做如下的操作:

update t1 set id=0 where id=2

查看数据页上的存储情况:

dbcc page(5,1,475032,1)

结果如下:
Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 96 (0x60)
0 (0x0) - 147 (0x93)

对表t2做如下的操作:

delete from t2 where id=2
insert into t2 select 0,'bb'

查看数据页上的存储情况:

dbcc page(5,1,152432,1)

结果如下:
Row - Offset
2 (0x2) - 130 (0x82)
1 (0x1) - 96 (0x60)
0 (0x0) - 147 (0x93)

可以看到此时的update为非现场更新,所以update后数据的存储位置发生了改变。
这种情况update和先delete后insert是相当的。

其实在更改聚集索引键列的时候,也可能发生现场更新。比如有3条记录分别为1、2、5,我们把其中的2更改为了3,由于3是在1和5之间的数字,所以在更改为3后,这条记录还是会存储在1和5之间,所以就是现场更新了。

从以上的测试情形来看,update并不是简单的先delete后insert了,如果考虑锁的情形,那就更不是了。

--王成辉原创
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐