读书笔记之MERGE 语句使用

常用语法

MERGE INTO <target table> AS TGT
USING <SOURCE TABLE> AS SRC
  ON <merge predicate>
WHEN MATCHED [AND <predicate>]                  -- 允许两个子句:
  THEN <action>                                 -- UPDATE 和 DELETE
WHEN NOT MATCHED [BY TARGET] [AND <predicate>]  -- 允许一个子句:
  THEN INSERT...                                –- INSERT
WHEN NOT MATCHED BY SOURCE [AND <predicate>]    -- 允许两个子句:
  THEN <action>;                                -- UPDATE 和 DELETE

建立目标表

-- clear table and reset sequence if the already exist
--TRUNCATE TABLE Sales.MyOrders;
--ALTER SEQUENCE Sales.SeqOrderIDs RESTART WITH 1;

-- create table and sequence if they don't already exist
IF OBJECT_ID(N'Sales.MyOrders', N'U') IS NOT NULL DROP TABLE Sales.MyOrders;
IF OBJECT_ID(N'Sales.SeqOrderIDs', N'SO') IS NOT NULL DROP SEQUENCE Sales.SeqOrderIDs;

CREATE SEQUENCE Sales.SeqOrderIDs AS INT
  MINVALUE 1
  CYCLE;

CREATE TABLE Sales.MyOrders
(
  orderid INT NOT NULL
    CONSTRAINT PK_MyOrders_orderid PRIMARY KEY
    CONSTRAINT DFT_MyOrders_orderid
      DEFAULT(NEXT VALUE FOR Sales.SeqOrderIDs),
  custid  INT NOT NULL
    CONSTRAINT CHK_MyOrders_custid CHECK(custid > 0),
  empid   INT NOT NULL
    CONSTRAINT CHK_MyOrders_empid CHECK(empid > 0),
  orderdate DATE NOT NULL
);

源表例子,这里使用参数作为数据源。

-- SELECT without FROM
DECLARE
  @orderid   AS INT  = 1,
  @custid    AS INT  = 1,
  @empid     AS INT  = 2,
  @orderdate AS DATE = '20120620';

SELECT *
FROM (SELECT @orderid, @custid, @empid, @orderdate )
      AS SRC( orderid,  custid,  empid,  orderdate );
GO
      
-- table value constructor
DECLARE
  @orderid   AS INT  = 1,
  @custid    AS INT  = 1,
  @empid     AS INT  = 2,
  @orderdate AS DATE = '20120620';

SELECT *
FROM (VALUES(@orderid, @custid, @empid, @orderdate))
      AS SRC( orderid,  custid,  empid,  orderdate);

实际例子

例1 ,很多人用下面这段来更新仓库中的表 (更新存在的,插入不存在的)

-- update where exists (only if different), insert where not exists,
-- delete when exists in target but not in source
DECLARE
  @orderid   AS INT  = 1,
  @custid    AS INT  = 1,
  @empid     AS INT  = 2,
  @orderdate AS DATE = '20120620';

MERGE INTO Sales.MyOrders WITH (HOLDLOCK) AS TGT          --这里 HOLDLOCK或者SERIALIZABLE作用都是一样的,防止MERGE冲突
USING (VALUES(@orderid, @custid, @empid, @orderdate))        --比如某个主键ID不存在于目标表。有两个进程P1和P2使用MERGE同时处理这个ID
       AS SRC( orderid,  custid,  empid,  orderdate)       --P1插入了这个ID的同时,P2也插入该ID,此时P2就会因违背主键约束而失败。
  ON SRC.orderid = TGT.orderid
--WHEN MATCHED THEN UPDATE                                  --这里有一个性能问题,如果第二次执行该语句,碰到ID一模一样数据,会再一次更新。
WHEN MATCHED AND (   TGT.custid    <> SRC.custid           --这样会损耗性能,可以额外加判断来减少性能损失。
                  OR TGT.empid     <> SRC.empid
                  OR TGT.orderdate <> SRC.orderdate) THEN UPDATE  
  SET TGT.custid    = SRC.custid,     
      TGT.empid     = SRC.empid,
      TGT.orderdate = SRC.orderdate
WHEN NOT MATCHED THEN INSERT                              --如果目标表不存在则插入
  VALUES(SRC.orderid, SRC.custid, SRC.empid, SRC.orderdate);
WHEN NOT MATCHED BY SOURCE THEN                           --如果目标表存在但源表不存在则删除
  DELETE;
OUTPUT
  $action AS the_action,
  COALESCE(inserted.orderid, deleted.orderid) AS orderid

注意,有时候还需要处理NULL值

TGT.custid = SRC.custid OR (TGT.custid IS NULL AND SRC.custid IS NOT
NULL) OR (TGT.custid IS NOT NULL AND SRC.custid IS NULL).

例2 ,注意ON 子句规则

MERGE INTO Sales.MyOrders AS TGT
USING Sales.Orders AS SRC
  ON  SRC.orderid = TGT.orderid
  AND shipcountry = N'Norway'
WHEN MATCHED AND (   TGT.custid    <> SRC.custid
                  OR TGT.empid     <> SRC.empid
                  OR TGT.orderdate <> SRC.orderdate) THEN UPDATE
  SET TGT.custid    = SRC.custid,
      TGT.empid     = SRC.empid,
      TGT.orderdate = SRC.orderdate
WHEN NOT MATCHED THEN INSERT
  VALUES(SRC.orderid, SRC.custid, SRC.empid, SRC.orderdate);

以上代码, ON子句有一个谓语  shipcountry = N'Norway' ,当第一次顺利执行。但是第二次就会报错。

image

之所以报错,是因为ON字句并不会过滤数据,如果shipcountry 不是Norway,则直接执行 NOT MATCHED ,此时因为目标表里面已经有了数据,导致了主键约束错误

解决方法只能事先过滤,然后再执行MERGE

-- 使用CTE
WITH SRC AS
(
  SELECT *
  FROM Sales.Orders
  WHERE shipcountry = N'Norway'
)
MERGE INTO Sales.MyOrders AS TGT
USING SRC
  ON  SRC.orderid = TGT.orderid 
WHEN MATCHED AND (   TGT.custid    <> SRC.custid
                  OR TGT.empid     <> SRC.empid
                  OR TGT.orderdate <> SRC.orderdate) THEN UPDATE
  SET TGT.custid    = SRC.custid,
      TGT.empid     = SRC.empid,
      TGT.orderdate = SRC.orderdate
WHEN NOT MATCHED THEN INSERT
  VALUES(SRC.orderid, SRC.custid, SRC.empid, SRC.orderdate);

-- 使用派生表
MERGE INTO Sales.MyOrders AS TGT
USING ( SELECT *
        FROM Sales.Orders
        WHERE shipcountry = N'Norway' )
 AS SRC
  ON  SRC.orderid = TGT.orderid 
WHEN MATCHED AND (   TGT.custid    <> SRC.custid
                  OR TGT.empid     <> SRC.empid
                  OR TGT.orderdate <> SRC.orderdate) THEN UPDATE
  SET TGT.custid    = SRC.custid,
      TGT.empid     = SRC.empid,
      TGT.orderdate = SRC.orderdate
WHEN NOT MATCHED THEN INSERT
  VALUES(SRC.orderid, SRC.custid, SRC.empid, SRC.orderdate);

例3 XML处理

SELECT  ProductID ,
        Name
INTO    Products
FROM    Production.Product

SELECT  ProductID AS "@id" ,
        Name AS "@name"
FROM    Products
WHERE   Name LIKE '_A%'
FOR     XML PATH('product') ,
            ROOT('products');

DECLARE @Xml XML = N'
<products>
  <product id="843" name="Cable Lock" />
  <product id="873" name="Patch Kit/8 P11atches" />
  <product id="875" delete="true" name="Racing Socks, L" />
  <product id="874" name="Racing Socks, M" />
  <product id="846" name="Taillights - Battery-Pow1ered" />
  <product name="Wdsfe - 30 oz." />
</products>';
WITH    src
          AS ( SELECT   xt.xc.value('@id', 'INT') AS ProductID ,
                        xt.xc.value('@name', 'NVARCHAR(1000)') AS Name ,
                        ISNULL(xt.xc.value('@delete', 'BIT'), 0) AS DoDelete
               FROM     @Xml.nodes('/products/product') AS xt ( xc )
             )
    MERGE INTO Products AS dest
    USING src
    ON src.ProductID = dest.ProductID
    WHEN NOT MATCHED THEN
        INSERT ( Name )
        VALUES ( src.Name )
    WHEN MATCHED AND src.DoDelete = 0 THEN
        UPDATE SET
               Name = src.Name
    WHEN MATCHED AND src.DoDelete = 1 THEN
        DELETE ;

转载于:https://www.cnblogs.com/haseo/p/MERGE.html

Greenplum数据库中没有直接支持MERGE INTO语句的功能。然而,你可以使用其他方法来实现类似的效果。一种常见的方法是使用INSERT INTO和UPDATE语句结合使用。 首先,你可以使用INSERT INTO语句将数据插入到目标表中。如果目标表中已经存在相同的记录,则会引发唯一键冲突错误。在这种情况下,你可以使用UPDATE语句执行更新操作。 以下是一个示例,演示如何在Greenplum数据库中模拟MERGE INTO语句的功能: ```sql -- 创建目标表 CREATE TABLE target_table ( id INT PRIMARY KEY, value VARCHAR(255) ); -- 创建临时表存储要插入的数据 CREATE TEMPORARY TABLE temp_table ( id INT, value VARCHAR(255) ); -- 插入数据到临时表 INSERT INTO temp_table (id, value) VALUES (1, 'Value 1'), (2, 'Value 2'), (3, 'Value 3'); -- 将临时表中的数据插入到目标表中 INSERT INTO target_table (id, value) SELECT id, value FROM temp_table ON CONFLICT (id) DO UPDATE SET value = excluded.value; -- 删除临时表 DROP TABLE temp_table; ``` 在上面的示例中,我们首先创建了目标表和临时表。然后,我们将要插入的数据插入到临时表中。最后,我们使用INSERT INTO和SELECT语句将临时表中的数据插入到目标表中。如果插入过程中发生唯一键冲突,我们使用ON CONFLICT子句执行更新操作。 请注意,这只是一种模拟MERGE INTO语句的方法,并不是Greenplum数据库的官方支持。你可以根据自己的需求和具体的数据操作场景进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值