众所周知,SQL SERVER在更新数据时有两个辅助表(deleted和inserted)供我们使用,但是在数据跟新时,真的是先删除记录在插入记录吗?

让我们来测试下:

PS:以下测试在简单恢复模式的数据库上运行,使用checkpoint来截断日志,使用TRACE FLAG 3505来阻止系统checkpoint。

测试方式:更新数据,查看日志记录

DROP TABLE TB2
GO
CREATE TABLE TB2
(C1 INT PRIMARY KEY IDENTITY(1,1),C2 NVARCHAR(1000),C3 BIGINT
)
INSERT INTO TB2(C2,C3)
SELECT name,OBJECT_ID FROM sys.all_columns
GO
CHECKPOINT
--=====================================
--更新数据
UPDATE  TB2
SET C3=0
WHERE C1=4--=====================================
--查看生成的日志
SELECT F.[Current LSN],
F.Operation,
F.Context,
F.[Transaction ID],
F.AllocUnitName,
F.[Page ID]
FROM fn_dblog(NULL,NULL) AS F
ORDER BY [Current LSN] DESC

再次测试

DROP TABLE TB2
GO
CREATE TABLE TB2
(C1 INT PRIMARY KEY IDENTITY(1,1),C2 NVARCHAR(1000),C3 BIGINT
)
INSERT INTO TB2(C2,C3)
SELECT name,OBJECT_ID FROM sys.all_columns
GO
CHECKPOINT
--=====================================
--更新数据
UPDATE  TB2
SET C2=REPLICATE('AB',40)
WHERE C1=4--=====================================
--查看生成的日志
SELECT F.[Current LSN],
F.Operation,
F.Context,
F.[Transaction ID],
F.AllocUnitName,
F.[Page ID],
F.[Slot ID]
FROM fn_dblog(NULL,NULL) AS F
ORDER BY [Current LSN] DESC

测试结论:
1>在更新时,如果更新后的数据不会造成数据移动(如页拆分情况)时,直接修改该行数据即可,而如果造成数据移动如页拆分的话,则采用先删除再插入的方式移动一些数据行(移动的数据行可能不是要更新数据行),来为要更新的行腾出足够空间,来更新数据行。
2>无论采用何种方式更新数据,在触发器中都能使用DELETED和INSERTED表来获取更新前和更新后数据。

3>数据操作导致页拆分时,页拆分操作会被当做单独事务处理,这样可以在回滚数据操作时避免回滚页拆分。(感谢JentleWang指点)。

--====================================================================

在上面的操作中,更新操作导致数据的变化,因此需要写入日志,记录数据变化,那如果更新操作不导致数据变化呢?

让我们再来测试下:

测试方式:在表TB2中导入5000+数据,表中只有4条数据的C2列值为456,其余行的C2列值为123,我们尝试更新整表数据的C2列值为123(针对5000+数据更新,但实际发生数据变化只有4行)

DROP TABLE TB2
GO
CREATE TABLE TB2
(C1 INT PRIMARY KEY IDENTITY(1,1),C2 BIGINT
)
INSERT INTO TB2(C2)
SELECT 123 FROM sys.all_columns
GO
UPDATE TB2
SET C2=456
WHERE C1<5
GO
CHECKPOINT
--=====================================
--更新数据
UPDATE TB2
SET C2=123--=====================================
--查看生成的日志
SELECT F.[Current LSN],
F.Operation,
F.Context,
F.[Transaction ID],
F.AllocUnitName,
F.[Page ID],
F.[Slot ID]
FROM fn_dblog(NULL,NULL) AS F
ORDER BY [Current LSN] DESC

可以发现,虽然提示消息显示5134行数据受影响,但实际上只有四条日志记录被写入日志,这四条日志记录分别对应发生数据变化的行(依据page ID和solt ID来确定)。

出查看日志外,我们也可以使用数据胀页来查看

--=============================================
--确保表中只有4条数据的C2列不为123
UPDATE TB2
SET C2=123
GO
UPDATE TB2
SET C2=456
WHERE C1<5
GO
CHECKPOINT
GO
--=============================================
--更新前查看数据胀页
--PS: 在checkpoint结束后立即检查胀页,会发现还有
--少量胀页存在,需要等待一段时间
WAITFOR DELAY '0:0:10'
GO
SELECT * FROM sys.dm_os_buffer_descriptors
WHERE database_id = DB_ID() AND is_modified = 1
ORDER BY page_id;
--=====================================
--更新数据
UPDATE TB2
SET C2=123
--=============================================
--更新后查看数据胀页
SELECT * FROM sys.dm_os_buffer_descriptors
WHERE database_id = DB_ID() AND is_modified = 1
ORDER BY page_id;

运行上面code会发现,在表TB2进行整表更新后,只有一个数据胀页(表TB2共有14个数据页),由此我们也可以推断出只有部分数据页受影响。

测试结论:在数据更新时,如果当前行数据没有发生变化,那么不会在日志中记录该行数据,也不会因此将该行所在的页标示为胀页。

PS: 上述结论基于INT/DATETIME/CHAR/VARCHAR/NCHAR/NVARCHAR类型进行测试得出,在对TEXT/NTEXT测试时发现,即使值未发生改变,仍产生日志和数据胀页。

--================================================

关于deleted和inserted,最常见的应用场景就是在触发器中,使用在OUTPUT中较少,做个demo:

--=======================================
--创建测试表
CREATE TABLE TB1
(ID INT IDENTITY(1,1) PRIMARY KEY,C1 NVARCHAR(200)
)
GO
--=======================================
--向测试表中导入100条数据
INSERT INTO TB1(C1)
SELECT TOP(100)
name
FROM sys.all_columns
GO
--=======================================
--更新测试表中10条数据,并找出被更新的行的ID
DECLARE @Tem TABLE
(
ID INT
)
UPDATE TOP(10) TB1
SET C1='ABC'
OUTPUT inserted.ID INTO @Tem(ID)SELECT * FROM @Tem

在上面的demo中,我们可以很容易地利用deleted和inserted来查看受影响的数据行。利用inserted,我们可以在批量插入自增表中获取所有生成的自增值(@@IDENTITY只能获取最后一行的值)。

对于deleted和inserted,会记录操作中影响的所有行(即使行上的值未发生变化),同样对于在触发器中使用的UPDATE()函数,该函数同样只判断列是否受影响,而不判断值是否改变。

--================================================

黄色背景中描述的删除插入以及更新均指在数据页上的操作,请勿和DML语句中的操作相混淆。

网上流传的版本:

UPDATE操作会被转换成两种方式中的一种:

1. XXX条件下,直接update数据行

2. XXX条件下,先删除数据旧行,再插入数据新行

对于这种版本,我曾经也认为时对的,并且记录在笔记本中,时间太久,找不到原出处。先仔细推敲测试,方觉得不对头,诸君可以发表下看法。

个人测试结论:

1.对于固定存储空间的数据列进行更新时,由于数据长度肯定不会发生变化,因此直接修改数据,slotID 不会发生变化,行在页上的位置不发生变化。

2.对于不固定存储空间的数据列进行更新时,可能会直接update数据,也可能先删除在插入数据行(即使更新后的数据长度比更新前要小,也可能会导致先删除再更新的情况),slotID 不会发生变化,删除插入会导致行在页上的位置发生变化,即行在页上的偏移量发生变化。

3.对于不固定存储空间的数据列进行更新时,即使更新行所在位置后面有充足空间(未被其他数据行使用),也可能发生删除插入的情况。

4. 无论是在原位置上直接更新还是删除插入导致行偏移量变化,都不会记录日志

有兴趣的同志可以阅读下这篇:http://www.cnblogs.com/wwwwgou/

--================================================

参考来源:

http://www.cnblogs.com/nzperfect/archive/2012/12/12/2814554.html

--================================================

妹子来啦

转载于:https://www.cnblogs.com/TeyGao/p/3625585.html

曲苑杂坛--数据库更新探秘相关推荐

  1. 曲苑杂坛--收缩数据库文件

    很多人在删除大量数据后收缩数据库,却发现没法收缩到预期效果. 由于使用DBCC SHRINKFILE来收缩数据文件时,是针对数据区来收缩,因此可以先使用DBCC SHOWFILESTATS来查看文件中 ...

  2. 曲苑杂坛--修改数据库名和文件组名

    /* 该脚本示例如何完整的修改一个数据库的名称. 数据库为原名称为DB_BEIJING,需要修改成DB_SHANGHAI nzperfect 2012.12.19 */--判断是否存在同名的数据库,以 ...

  3. 【c苑杂坛之小游戏系列】 三子棋游戏

    [c苑杂坛之边学边玩] 三子棋游戏带你初识C语言 1.游戏说明 2.效果展示 3.游戏代码 1.游戏说明 三子棋游戏相信大家都知道,在这里不去讲述游戏背景,对本代码实现的三子棋游戏进行一定的说明. 博 ...

  4. pd 生成mysql 脚本_PowerDesigner 如何生成数据库更新脚本

    最近在学习使用PowerDesigner 这个数据库设计工具,发现真的很强大,可以做很多事情,其中就涉及到如果数据库要进行更新了怎么办,主要是增加表,最麻烦的是修改字段名称,增加字段等操作,遇到主要的 ...

  5. Scude导入MySQL_FM2017_FMF赛季更新和真实修正数据库[更新至9.9,超过89000个更新]

    FM2017_FMF赛季更新和真实修正数据库[更新至9.9,超过89000个更新]FM2017_FMF赛季更新和真实修正数据库[更新至9.9,超过89000个更新](2) 这是国外玩家制作的一款FM2 ...

  6. Oracle数据库更新时间的SQL语句

    ---Oracle数据库更新时间字段数据时的sql语句 ---格式化时间插入 update t_user u set u.name='pipi',u.modifytime=to_date('2015- ...

  7. phpcms如何修改数据库图片地址?域名变更后图片附件路径只能手动修改数据库更新?

    phpcms如何修改数据库图片地址?域名变更后图片附件路径只能手动修改数据库更新? phpcms数据库中,存放图片路径地址的那个表,是什么表? 举例说明: 以前的域名是 abc .com.文章里图片附 ...

  8. mysql 数据库 更新_mysql数据库更新

    在使用mysql数据库的时候,A方使用一个版本,B方在使用一个版本数据库进行开发使用,B方在开发的时候,有新的需求,需要添加表字段和所需要的表.但是A方已经在使用之前的版本数据库并且数据库里面有真实的 ...

  9. mysql没有实体框架_NET实体框架数据库更新未在MySQL数据库中创...

    我使用MySql作为官方连接提供程序的数据库.我正在Mac上的下一个项目示例(asp.net core 1.0)中进行尝试: public class BloggingContext : DbCont ...

  10. 微软ASP.NET站点部署指南(11):部署SQL Server数据库更新

    1. 综述 本章节展示的是如何向完整版SQL Server 数据库部署一个数据库升级.与第9章的数据库升级部署有所不同(第9章是部署到SQL Server Compact数据库). 提醒:如果根据本章 ...

最新文章

  1. Serializable 都这么牛逼了,Parcelable 还要你何用?
  2. 打印SAP ABAP web service call完整的payload
  3. P4336-[SHOI2016]黑暗前的幻想乡【矩阵树定理,容斥】
  4. swift版的StringAttribute
  5. echarts 3d饼图_Echarts 使用教程 1 基本使用方法
  6. %@ include file=和jsp:include file=区别
  7. 201507152326_《Javascript实现跨域有4种方法——介绍jsonp和html5方法》
  8. C语言 实现面向对象
  9. 领域的初学者--推荐的一本书
  10. Kubernetes Dashboard 终结者:KubeSphere
  11. CodeForces - 1313C2 Skyscrapers (hard version)(单调栈+dp)
  12. 常州大学/教务系统/教室相关
  13. NOIP2016提高组 第一天第二题 天天爱跑步running 题解
  14. Delphi访问网络共享文件夹
  15. 网站权重是什么意思?
  16. 对称加密和非对称加密!
  17. OSChina 周二乱弹 ——快晒晒你们公司的圣诞小姐姐啊!
  18. stm32【GT30L32S4W字库芯片】
  19. 关于个人电脑C盘清理的一些小整合
  20. 解决EXP-00003问题

热门文章

  1. 适应iPhone5的尺寸
  2. Ubuntu18.04添加vim配置
  3. Anaconda添加清华镜像源
  4. Flink 实战问题(五):The transaction timeout is larger than the maximum value allowed by the broker
  5. Oracle必读好书推荐
  6. 大数据翻页_大数据分页实现与性能优化
  7. Android中视频播放以及解码
  8. 常用开发软件下载网站集合
  9. 用java编写国际象棋
  10. html td 跨两个,【单选题】在HTML中,td标签的( )属性用于创建跨多个行的单元格。...