1 个需求,2 种写法, 3 层境界
点击蓝色“有关SQL”关注我哟
加个“星标”,天天与10000人一起快乐成长
图 | L
1 个需求
外人看来一个简单的需求:
把某个人的身份信息,合并到用户表里。
思路再简单不过:如果这个人存在表里,那就更新;如果他/她不在,那就新建。
2 种写法
很多朋友,写这类 SQL,手到擒来。无非就是 Update 和 Insert.
先判断下这个人,在不在表里:
IF Exists(SELECT TOP 1 1 FROM User WHERE UserName = @var_UserName)
BEIGN
UPDATE User SET XXX = XXX WHERE UserName = @var_UserName
END
ELSE
BEGIN
INSERT INTO User ( XXX,XXX) ) VALUES(xxx,xxx)
END
但,SQL 表达可以更简单,请出今天的主角:Merge
MERGE INTO User
USING (xxx) AS UserUpdate
on User.UserName = UserUpdate.UserName
WHEN MATCHED THEN UPDATE SET UserAddress = UserUpdate.UserAddress
WHEN NOT MATCHED THEN INSERT(XXX,XXX) VALUES(xxx,xxx)
Merge 语句同样实现了 UPDATE/INSERT 组合的功能。
解释下:
USING(xxx) AS UserUpdate ON:
xxx 表示用来更新的准备数据,其形式可以是一条SELECT 语句,也可以是一条 VALUES构造语句(适用于SQL Server)。
ON 在这里,指定了匹配条件
MATCHED:
当匹配条件满足,执行数据更新
NOT MATCHED :
当匹配条件不满足,执行数据新建
3 层境界
到这里还没完。
能写出第一类 UPDATE/INSERT 算是基础过关。如果数据库访问量不大,自然没毛病。
但,UPDATE/INSERT 并不安全。
如果在判断 EXISTS 同时,该用户被其他人新建,则会产生冲突。所以,加上 BEGIN TRANS 来发起事务控制,将其他用户操作隔离开来。这是第二境界。
Merge 就不需要这份考量。它是一个语句,从语句层面完成了事务控制。
但 Merge 虽强,碰到大数据量,写法依旧单薄。尤其在 Merge 操作中,更新了上百万行,产生大量日志的同时,还会锁表,对数据库及其不友好。
怎么办?改批次!
MERGE TOP(10000) USER
USING (xxx) AS UserUpdate
on User.UserName = UserUpdate.UserName
WHEN MATCHED THEN UPDATE SET UserName = UserUpdate.UserName
WHEN NOT MATCHED THEN INSERT(XXX,XXX) VALUES(xxx,xxx)
每 10000 条数据做一次 Merge,可以完美解决。
这是第三层考量。
那,为什么要用 Merge 而不用 UPDATE/INSERT 组合呢?原因有 2:
Merge 单条语句实现了事务控制,上面已说 Merge 是轻量更新:本例用一条数据解释了 Merge,但实际情况,Merge 可以实现表对表的合并,当两表数据量都大时, UPDATE/INSERT 组合,产生了两次对比查询,和两次日志更新,但 Merge 只需一次。
往期精彩: