MERGE 与 WHERE 子句

MERGE with WHERE clause

考虑 table:

中存在的数据

客户

| CustomerID | Name            | Status             |
|------------|-----------------|--------------------|
|          1 | Ian Boyd        | Killed             |
|          2 | Shelby Hawthorn | Booked             |

我想 MERGEd 到 Customers table:

| CustomerID | Name            | Status             |
|------------|-----------------|--------------------|
|          1 | Ian Boyde       | Waiting            | name has 'e' on the end
|          2 | Shelby Blanken  | Waiting            | different last name
|          3 | Jessica Bogden  | Waiting            | totally new row

所以我可以想出近似的伪代码 MERGE 语句:

MERGE Customers USING (
    SELECT CustomerID, Name, 'Waiting' FROM Staging) foo
ON Customers.CustomerID = foo.CustomerID
WHEN MATCHED THEN
   UPDATE SET Name = foo.Name, Status = foo.Status
WHEN NOT MATCHED BY TARGET THEN
   INSERT (Name, Status) 
   VALUES (Name, Status);

这将合并它们:

| CustomerID | Name            | Status             |
|------------|-----------------|--------------------|
|          1 | Ian Boyde       | Waiting            | Last name spelling updated
|          2 | Shelby Blanken  | Waiting            | Last name changed
|          3 | Jessica Bogden  | Waiting            | New row added

但只更新了一些

除了需要注意的是,我不想为 Booked 的客户更新任何现有行。换句话说,我希望最终结果是:

| CustomerID | Name            | Status             |
|------------|-----------------|--------------------|
|          1 | Ian Boyde       | Waiting            | updated existing row spelling
|          2 | Shelby Hawthorn | Booked             | not updated because they're booked
|          3 | Jessica Bogden  | Waiting            | inserted new row

我的第一个猜测是 UPDATE 有一个 where 子句:

MERGE Customers USING (
    SELECT CustomerID, Name, 'Waiting' FROM Staging) foo
ON Customers.CustomerID = foo.CustomerID
WHEN MATCHED THEN
   UPDATE SET Name = foo.Name, Status = foo.Status
   WHERE Status <> 'Booked' -- <--------- it's the matching row; but don't update it
WHEN NOT MATCHED BY TARGET THEN
   INSERT (Name, Status) 
   VALUES (Name, Status);

但这不是有效的语法。

我的第二个猜测是将条件添加到 ON 子句:

MERGE Customers USING (
    SELECT CustomerID, Name, 'Waiting' FROM Staging) foo
ON Customers.CustomerID = foo.CustomerID
AND Customers.Status <> 'Booked'
WHEN MATCHED THEN
   UPDATE SET Name = foo.Name, Status = foo.Status
   WHERE Status <> 'Booked' --it's the matching row; but don't update it
WHEN NOT MATCHED BY TARGET THEN
   INSERT (Name, Status) 
   VALUES (Name, Status);

但现在该行不匹配,它们将根据 not matched by target 规则插入:

| CustomerID | Name            | Status             |
|------------|-----------------|--------------------|
|          1 | Ian Boyde       | Waiting            | updated existing row
|          2 | Shelby Hawthorn | Booked             | not matched bcause booked
|          3 | Jessica Bogden  | Waiting            | inserted new row
|          4 | Shelby Blanden  | Waiting            | Mistakenly inserted because not matched by target

走出困境的出路是什么?

合并语句由 USING 子句驱动。

  • USING 中 匹配 现有行的行导致现有行更新。
  • USING 中的行不匹配 现有行导致创建新行
  • 不在 USING 子句中的行不能影响数据库中的行

如果您不希望更新现有行,请确保其匹配行永远不会进入 USING 中的语句所呈现的结果集中。这可能意味着在 USING 中进行连接。这很好

示例:

MERGE Customers USING (
  SELECT 
    s.CustomerID, 
    s.Name,
    'Waiting' as Stat
  FROM 
    Staging s
    INNER JOIN Existing e on s.CustomerId = e.CustomerId
  WHERE
    e.Status <> 'Booked' --ignore all existing booked rows
) foo
...

using 语句中的这个连接确保与现有 "Booked" 行相关的暂存行永远不会进入 USING 生成的结果集中。因此它不会导致更新或插入

关键是要确保记录落入MATCHED逻辑,否则会通过NOT MATCHED逻辑生成新行。

为此,我们使用您的代码将您的条件添加到 MATCHED 逻辑中:

MERGE Customers USING (
    SELECT CustomerID, Name, 'Waiting' FROM Staging) foo
ON Customers.CustomerID = foo.CustomerID
WHEN MATCHED AND Customers.Status <> 'Booked' THEN
   UPDATE SET Name = foo.Name, Status = foo.Status
WHEN NOT MATCHED BY TARGET THEN
   INSERT (Name, Status) 
   VALUES (Name, Status);

这告诉合并匹配 CustomerID 上的所有内容。当它找到匹配项时,如果 Status <> 'Booked'

,则告诉它仅 运行 更新