2011-09-02 37 views
1

说我有一个MERGE语句,看起来像这样:SQL Server是否足够聪明,在真正需要时不会执行IO操作?

merge TableA as target 
using (select Id, Description, UnitCost 
     from TableB) 
     as source (Id, Description, UnitCost) 
on (target.Id = source.Id)    
when MATCHED then 
    update set Id = source.Id, 
       Description = source.Description, 
       UnitCost = Source.UnitCost 
when NOT MATCHED then 
    insert (Id, Description, UnitCost) 
    values (source.Id, source.Description, source.UnitCost); 

当我运行这个它告诉我多少行受到影响。如果我运行它,并且我知道源和目标完全相同,我仍然会收到一条消息,告诉我x行受到了影响。在我的情况下,大约200行。 SQL Server是否将相同的数据重新写入磁盘?

200行不算什么,可以很容易地重写,不影响SQL Server的性能。但是,如果我有一个包含500,000多行和大量索引的合并语句,那么重新更新表中的所有数据将会变得非常昂贵。

我是否需要检查数据是否先改变(至少在性能可能成为问题的情况下)?

如果是这样,我该怎么做一个合并的声明(也许使用我的例子上面)?

+1

您可能会发现[本文相关(http://sqlblog.com/blogs/paul_white/archive/2010/08/11/the_2D00_impact_2D00_of_2D00_update_2D00_statements_2D00_that_2D00_don_2D00_t_2D00_change_2D00_data.aspx) –

回答

2
merge TableA as target 
using (select Id, Description, UnitCost 
     from TableB) 
     as source (Id, Description, UnitCost) 
on (target.Id = source.Id)    
when MATCHED AND (ID <> source.ID OR Description <> source.Description OR UnitCost <> Source.UnitCost) then 
    update set Id = source.Id, 
       Description = source.Description, 
       UnitCost = Source.UnitCost 
when NOT MATCHED then 
    insert (Id, Description, UnitCost) 
    values (source.Id, source.Description, source.UnitCost); 

您可以添加条件搜索子句到匹配语句,这基本上检查,以确保实际上已经发生了变化。不知道这是否会更快,但不会更新不需要更新的行。

如果您需要更多的信息来检查文档MERGE (T-SQL)

+0

获取更痛苦一点如果任何列可以为空 –

+0

@马丁一切得到更痛苦的可空列:( – msarchet

+0

顺便说一句[刚想到一种缓解那种痛苦的方式](http://stackoverflow.com/questions/7339905/checking-to-see-if-row-data-has -changed/7341058#7341058) –

2

SQL Server和任何缓冲池预写日志基于发动机的事项,更新不会做数据IO /删除/插入。自从ARIES论文发表以来,它一直如此,几乎所有现代关系数据库都将其祖先追溯到System-R和ARIES。

当一行被更新(并且包括插入和删除行)时,日志记录被添加到描述该更改的内存的日志缓冲区中,然后包含内存中的行的页面被更新。什么都不写入磁盘。执行继续。当事务提交时,生成一个新的日志记录,并且提交不能继续,直到所有的登录内存,直到并包括日志提交记录都被刷新到磁盘。这是更新被允许继续进行所需的唯一强制IO。如果您更新500k行,则在一条语句中,系统将只需等待所有500k行更新后的日志清空

checkpoints期间,内存中的数据会定期写入磁盘。