SqlServer中的merge操作(转载)

SqlServer中的merge操作(转载)

今天在一个存储过程中看见了merge这个关键字,第一个想法是,这个是配置管理中的概念吗,把相邻两次的更改合并到一起。后来在technet上搜索发现别有洞天,原来是另外一个sql关键字,t-sql的语法还是相当地丰富的。本篇是一篇学习笔记,没有什么新意,这里给出technet上的地址连接供大家参考权威:http://technet.microsoft.com/zh-cn/library/bb510625.aspx,这里具体的语法不去深究了,只是把几个例子实际运行,剖析一番。

  

使用merge同时执行insert和update操作

我们经常会有这样的需求,根据某个字段或多个字段查找表中的一行或多行数据,如果查找成功得到匹配项,更新其中的其他一个或多个字段;如果查找失败则将“某个字段或多个字段”作为新的一行中的数据插入到表中。第一种方法是先更新,然后根据@@rowcount判断是否有匹配项,如果没有则插入。先使用下面的 代码创建一个存储过程。

1 use AdventureWorks
2 go
3 createprocedure dbo.InsertUnitMeasure @UnitMeasureCodenchar(3),@Namenvarchar(25)
4 as
5 begin
6 set nocount on;
7 update Production.UnitMeasure set [email protected] [email protected]
8 if(@@ROWCOUNT=0)
9 begin
10 insertinto Production.UnitMeasure(Name,UnitMeasureCode)values(@Name,@UnitMeasureCode)
11 end
12 end
13 go

记得见过这样的笔试题目,要求是插入不存在的行,只要把上面语句中的update改成select就可以了,当时没有写出来,现在恍然大悟,也许是在考察@@ROWCOUNT的用法吧。这个语句也可以使用merge语句实现。下面我们使用merge关键字来修改这个存储过程。

1 alterprocedure dbo.InsertUnitMeasure @UnitMeasureCodenchar(3),@Namenvarchar(25)
2 as
3 begin
4 set nocount on
5 merge Production.UnitMeasure as target
6 using ([email protected],@Name) as source (UnitMeasureCode,Name)
7 on (target.UnitMeasureCode=source.UnitMeasureCode)
8 when matched thenupdateset Name=source.Name
9 whennot matched theninsert(UnitMeasureCode,Name)values(source.UnitMeasureCode,Name)
10 output deleted.*,$action,inserted.*into MyTempTable;
11 end
12 go

这个语句使用merge修改存储过程,这个语句中又出现我不太了解的关键字using和$action。Using是用来指定和表InsertUnitMeasure中相匹配的数据源,这里的数据源来自外部输入,是通过两个输入参数得到。$action可能是一个占位符,表示上面的when字句进行的操作。至于inserted.*和deleted.* 就是插入和删除的数据行了,这个我在其中一篇文章中也提到,他们有点类似类中的this关键字,过可以看看:SQL点滴14—编辑数据。注意为了记录修改的过程我们需要创建一个临时表#MyTempTable来跟踪修改过程,所以在调用这个存储过程之前我们需要新建这个表,语句如下:

1 createtable MyTempTable(
2 ExistingCode nchar(3),
3 ExistingName nvarchar(50),
4 ExistingDate datetime,
5 ActionTaken nvarchar(50),
6 NewCode nchar(3),
7 [NewName]nvarchar(50), 
8 NewDate datetime
9 )
10 Go

现在我们来执行下面的语句看看有什么样的结果:

1 exec InsertUnitMeasure @UnitMeasureCode=‘ABC‘,@Name=‘New Test Value1‘
2 EXEC InsertUnitMeasure @UnitMeasureCode=‘XYZ‘, @Name=‘Test Value‘;
3 EXEC InsertUnitMeasure @UnitMeasureCode=‘ABC‘, @Name=‘Another Test Valuea‘;
4 Go

首先使用语句:select * from Production.UnitMeasure order by ModifiedDate desc 来查看目标表中的数据变化如图1:

图1

这里虽然三次执行了存储过程,但是由于第一次和第三次的@UnitMeasureCode的值是相同的’ABC’所以第二次肯定是进行更新操作。所以最后表中新增了两条记录。然后使用下面的语句查看记录表MyTempTable中的跟踪信息如图2

图2

我们可以看到前面两条语句执行的是插入操作,所以原有的值都是空,因为在插入之前他们还不存在。第三条新型的是更新操作,更新UnitMeasureCode为’ABC’的记录。
 
  

使用merge在单个语句中执行insert和update操作

在AdventureWorks数据库中有ProductInventory表,存储的是存货信息,SalesOrderDetail表中存储的是订单信息,现在如果每天减去对SalesOrderDetail表中每种产品所下的订单数,更新ProductInventory表中的 Quantity列。如果随着时间推移订单数导致产品库存量下降到0或者更少,则从ProductInventory表中删除该产品对应的行。下面的语句创建一个存储过程实现上面的逻辑。

1 CREATEPROCEDURE Production.usp_UpdateInventory
2 @OrderDatedatetime
3 AS
4 MERGE Production.ProductInventory AS target
5 USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod
6 JOIN Sales.SalesOrderHeader AS soh
7 ON sod.SalesOrderID = soh.SalesOrderID
8 AND soh.OrderDate [email protected]
9 GROUPBY ProductID) AS source (ProductID, OrderQty)
10 ON (target.ProductID = source.ProductID)
11 WHEN MATCHED AND target.Quantity - source.OrderQty <=0
12 THENDELETE
13 WHEN MATCHED 
14 THENUPDATESET target.Quantity = target.Quantity - source.OrderQty, 
15 target.ModifiedDate =GETDATE()
16 OUTPUT $action, Inserted.ProductID, Inserted.Quantity, Inserted.ModifiedDate, Deleted.ProductID,
17 Deleted.Quantity, Deleted.ModifiedDate;
18 GO

这个语句比第一个要复杂一点,注意当匹配成功并且总量小于0的时候直接使用一个delete就可以将此条记录删除,output语句直接把操作结果输出,相当地神奇。最后运行下面的 语句得到如图3的结果。注意这个语句相当于将2003年5月1号的订单量减去。如果多次运行的话就相当于多减了一次,整个表中数据条数会减少的。

EXECUTE Production.usp_UpdateInventory ‘20030501‘

图3

  

借助派生源表,使用merge对目标表执行update和insert操作

这次我们已知有一些表数据,我们要和Sales.SalesReason这个表中的数据做对比,如果和SalesReason表中的Name字段匹配时就更新表中的ReasonType列,如果没有匹配项的时候就插入这一行新的数据。在这里是使用表值构造函数指定源表的多个行,使用表变量存储更新记录,注意表变量的使用范围。代码如下:

1 [email protected](Change varchar(20))
2 merge into Sales.SalesReason as target
3 using(values(‘Recommendation‘,‘Other‘),(‘Review‘,‘Marketing‘),(‘Internet‘,‘Promotion‘)) as source([NewName],NewReasonType)
4 on target.Name=source.[NewName]
5 when matched thenupdateset ReasonType=source.NewReasonType
6 whennot matched by target theninsert(Name,ReasonType) values ([NewName],NewReasonType)
7 output $action [email protected];
8 select Change,COUNT(*) as CountPerChange [email protected] Change

执行完上面的语句之后我们得到下面的结果说明执行了2次插入,1次更新,如图4。那么是不是这样的 呢,我们查看Sales.SalesReason这个表发现原来已经有’Review’这一条数据了,对它执行了更新,剩下的’Recommendation’,’Internet’执行的是插入操作。如果再次执行上面的语句就会得到UPDATE 3这样的结果,因为已经存在这三条数据了所以都执行UPDATE。

图4

  

将merge执行的结果插入到另外一个表中

我们还可以将merge操作得到的结果写入到另外一个表中,如下的语句将更新的每条数据信息写入到一个新建的表Production.UpdatedInventory中,代码如下:

1 INSERTINTO Production.UpdatedInventory
2 SELECT ProductID, LocationID, NewQty, PreviousQty 
3 FROM
4 ( MERGE Production.ProductInventory AS target
5 USING (SELECT ProductID, SUM(OrderQty) 
6 FROM Sales.SalesOrderDetail AS sod
7 JOIN Sales.SalesOrderHeader AS soh
8 ON sod.SalesOrderID = soh.SalesOrderID
9 AND soh.OrderDate BETWEEN‘20030701‘AND‘20030731‘
10 GROUPBY ProductID) AS source (ProductID, OrderQty)
11 ON target.ProductID = source.ProductID
12 WHEN MATCHED AND target.Quantity - source.OrderQty >=0 
13 THENUPDATESET target.Quantity = target.Quantity - source.OrderQty
14 WHEN MATCHED AND target.Quantity - source.OrderQty <=0 
15 THENDELETE
16 OUTPUT $action, Inserted.ProductID, Inserted.LocationID, Inserted.Quantity AS NewQty, Deleted.Quantity AS PreviousQty)
17 AS Changes (Action, ProductID, LocationID, NewQty, PreviousQty) WHERE Action =‘UPDATE‘;
18 GO

执行这个语句再查询表得到如下图5的结果,我们可以看到新的销售量总是比以前的销售量要少,因为执行一次就要减去订单量。

图5

这里我们只记录了更新的变化,如果想记录所有的操作可以去掉最后的一个限制条件WHERE Action = ‘UPDATE‘,那就要修改记录表的结构了,这个和第二个例子有些相似,只不过将记录在实际的表中,而第二个例子仅仅输出这些操作记录。

时间: 2024-10-03 22:18:24

SqlServer中的merge操作(转载)的相关文章

SQL点滴18—SqlServer中的merge操作,相当地风骚

原文:SQL点滴18-SqlServer中的merge操作,相当地风骚 今天在一个存储过程中看见了merge这个关键字,第一个想法是,这个是配置管理中的概念吗,把相邻两次的更改合并到一起.后来在technet上搜索发现别有洞天,原来是另外一个sql关键字,t-sql的语法还是相当地丰富的.本篇是一篇学习笔记,没有什么新意,这里给出technet上的地址连接供大家参考权威:http://technet.microsoft.com/zh-cn/library/bb510625.aspx,这里具体的语

MySQL与SqlServer中update操作同一个表问题

一 SqlServer中操作如下图 这个是没问题的. 二 MySQL中操作如下图 但是在MySQL中想实现这个功能如下图,但是出错了. 原来是MySQL中不支持子查询的 我们可以这样修改一下就可以实现它 看到没有,我仅仅在查询外面加了一层而已,却实现了. 代码如下: create PROCEDURE testp(in _id int) begin -- set @tt=(select id from usera where id>_id ); update usera set `names`='

SQL Server中的Merge Into

简介 Merge关键字是一个神奇的DML关键字.它在SQL Server 2008被引入,它能将Insert,Update,Delete简单的并为一句.MSDN对于Merge的解释非常的短小精悍:"根据与源表联接的结果,对目标表执行插入.更新或删除操作.例如,根据在另一个表中找到的差异在一个表中插入.更新或删除行,可以对两个表进行同步.",通过这个描述,我们可以看出Merge是关于对于两个表之间的数据进行操作的. 可以想象出,需要使用Merge的场景比如: 数据同步 数据转换 基于源表

SQLServer内核架构剖析 (转载)

SQL Server内核架构剖析 (转载) 这篇文章在我电脑里好长时间了,今天不小心给翻出来了,觉得写得很不错,因此贴出来共享. 不得不承认的是,一个优秀的软件是一步一步脚踏实地积累起来的,众多优秀的程序员呕心沥血,他们已经不是在简单的写代码,而是在创作一门艺术. 和前面提到的暴雪公司的发展相比他们有一个相同之处,即:他们只做经典.不能说他们集中的全世界最优秀的程序员,而实际上他们集中的是全世界最好的思想,并且付诸实践. 成功不是靠急于求成,而是靠远见.祝Microsoft SQL Server

sqlserver中select造成死锁

项目上线,准备验收前出现了一个严重的问题:很多select语句作为死锁的牺牲,大部分报表无法打开.这个问题影响范围很大所有的报表都无法访问,而我们的报表是放在电视上面轮播的,电视放在工厂里面,所以出现问题后,整个工厂都知道了. 解决这个问题比较曲折,首先是写SAP接口的同事发现了问题:SAP一直在传错误数据导致产量表被锁住.修改SAP传输的错误数据后,这个死锁的问题没有出现了.但是我查看生产环境服务器日志的时候,发现这个问题依然存在,由于客户没有提这个问题,我也就是没有理由要求花时间修改了,因为

git工作中的常用操作

上班开始,打开电脑,git pull:拉取git上最新的代码: 编辑代码,准备提交时,git stash:将自己编辑的代码暂存起来,防止git pull时与库中的代码起冲突,否则自己的代码就白敲了: 然后,git pull:拉取一下代码,与库中代码,做到同步,有冲突则解决冲突,如果省了这一步,别人有提交的代码,没有更新,自己提交就会报错,再走这一步,就会把别人的代码拉取出来,然后一起提交,就相当于你提交了自己的代码,也提交了别人的代码:还有,有时这样会使库中代码乱掉,别人的心血也会丢失,你就是罪

SQL Server -&gt;&gt; 尝试优化ETL中优化Merge性能

这几天突发想到在ETL中Merge性能的问题.思路的出发点是Merge到目标表需要扫描的数据太多,而现实情况下,假设应该是只有一小部分会被更新,而且这部分数据也应该是比较新的数据,比方说对于想FactOrders这样一张表,一些越日期越久远的订单可能不可能被更新.那么整个思路就是减小每次需要从磁盘加载目标表到内存中跟stage表进行merge操作的数据量.只是我存在着两个疑问,这也是我问题要进行下面实验的原因. 前提条件是:目标表通过日期进行分区. 第一个疑问:在索引的作用下,SQL Serve

CI中的AR操作

1 /** 2 * CI 中的 AR 操作 3 * @author zhaoyingnan 4 **/ 5 public function mAR() 6 { 7 /*************** 查询 *************/ 8 //select * from mp4ba limit 21,10; 9 //$objResult = $this->db->get('mp4ba', 10, 21); 10 //echo $this->db->last_query();die;

Git中的merge命令实现和工作方式

想象一下有如下情形:代码库中存在两个分支,并且每个分支都进行了修改,最后你想要将其中的一个分支合并到其他的分支中.个人博客网址 http://swinghu.github.com/ 那么要问合并的处理过程是怎么样的呢?Git是对每个分支,依据分支的历史数据按照序列化操作,还是它只是合并每个分支里文件的最后版本?这是一个问题,我想对git的merge操作有必要进行分析一下. 回忆一下,我们知道Git的版本库内部结构是以有向无环图(directed acyclic graph)组织起来的:每一次co