二个人同时insert操作mysql_使用merge同时执行insert和update操作

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 use AdventureWorks

2 go

3 create procedure dbo.InsertUnitMeasure @UnitMeasureCode nchar(3),@Name nvarchar(25)

4 as

5 begin

6 set nocount on;

7 update Production.UnitMeasure set Name=@Name where UnitMeasureCode=@UnitMeasureCode

8 if(@@ROWCOUNT=0)

9 begin

10 insert into Production.UnitMeasure(Name,UnitMeasureCode)values(@Name,@UnitMeasureCode)

11 end

12 end

13 go

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 alter procedure dbo.InsertUnitMeasure @UnitMeasureCode nchar(3),@Name nvarchar(25)

2 as

3 begin

4 set nocount on

5 merge Production.UnitMeasure as target

6 using (select @UnitMeasureCode,@Name) as source (UnitMeasureCode,Name)

7 on (target.UnitMeasureCode=source.UnitMeasureCode)

8 when matched then update set Name=source.Name

9 when not matched then insert(UnitMeasureCode,Name)values(source.UnitMeasureCode,Name)

10 output deleted.*,$action,inserted.* into MyTempTable;

11 end

12 go

48304ba5e6f9fe08f3fa1abda7d326ab.png

这个语句使用merge修改存储过程,这个语句中又出现我不太了解的关键字using和$action。Using是用来指定和表InsertUnitMeasure中相匹配的数据源,这里的数据源来自外部输入,是通过两个输入参数得到。$action可能是一个占位符,表示上面的when字句进行的操作。至于inserted.*和deleted.* 就是插入和删除的数据行了,这个我在其中一篇文章中也提到,他们有点类似类中的this关键字,过可以看看:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 create table 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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

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:

3fbf25ef1b27d8fe48dbed14c5f363f4.png

图1

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

f6bf561a667c99456d79c2a27fe69290.png

图2

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

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1CREATE PROCEDURE Production.usp_UpdateInventory

2 @OrderDate datetime

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 = @OrderDate

9 GROUP BY ProductID) AS source (ProductID, OrderQty)

10 ON (target.ProductID = source.ProductID)

11 WHEN MATCHED AND target.Quantity - source.OrderQty <= 0

12 THEN DELETE

13 WHEN MATCHED

14 THEN UPDATE SET 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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

EXECUTE Production.usp_UpdateInventory '20030501'

5fefec32f553a5614a62e4bdacd85846.png

图3

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 declare @SummaryOfChanges table(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 then update set ReasonType=source.NewReasonType

6 when not matched by target then insert(Name,ReasonType) values ([NewName],NewReasonType)

7 output $action into @SummaryOfChanges;

8 select Change,COUNT(*) as CountPerChange from @SummaryOfChanges group by Change

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

2d82c7b6e0fc54ece8292f28a3ea7405.png

图4

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 INSERT INTO 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 GROUP BY ProductID) AS source (ProductID, OrderQty)

11 ON target.ProductID = source.ProductID

12 WHEN MATCHED AND target.Quantity - source.OrderQty >= 0

13 THEN UPDATE SET target.Quantity = target.Quantity - source.OrderQty

14 WHEN MATCHED AND target.Quantity - source.OrderQty <= 0

15 THEN DELETE

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

e085f9eca378eaa557f7a1fff2812e44.png

图5

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

----demo

DECLARE @tem1 TABLE(

user_gid UNIQUEIDENTIFIER,

gid UNIQUEIDENTIFIER,

ruleName NVARCHAR(50),

ruleValue DECIMAL(11,4)

)

INSERT INTO @tem1

( user_gid,gid, ruleName, ruleValue )

VALUES

('39917B36-9663-42E5-A9E8-7CEB875EDF5F',NEWID(),'addressTempNum',9788.0000),

('39917B36-9663-42E5-A9E8-7CEB875EDF5F',NEWID(),'addressNum',978.0000)

MERGE INTO credit.record_rule_data AS a

USING @tem1 AS b ON a.rule_id =b.ruleName AND a.user_id ='39917B36-9663-42E5-A9E8-7CEB875EDF5F' AND a.auth_type =4

WHEN MATCHED THEN UPDATE SET a.rule_value =b.ruleValue ,update_time=GETDATE()

WHEN NOT MATCHED THEN

INSERT

(gid, create_time, update_time, data_version, user_id, auth_type, rule_id, rule_value)

VALUES

(NEWID(),GETDATE(),GETDATE(),'11','39917B36-9663-42E5-A9E8-7CEB875EDF5F',4,b.ruleName,b.ruleValue);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值