如何在一个语句中组合“删除”和“插入”操作?

Jac*_*las 5 oracle oracle-11g-r2

对于以下数据,我希望能够删除一些行并插入其他行并给出以下结果。这是否可以通过单个语句(例如使用merge语句)实现?

create table product(product_id integer primary key);
insert into product(product_id) values(1);
insert into product(product_id) values(2);
insert into product(product_id) values(3);

create table split( parent_id integer not null references product, 
                    child_id integer not null references product,
                    primary key(parent_id, child_id) );

insert into split(parent_id, child_id) values(1,2);
insert into split(parent_id, child_id) values(1,3);

create table sale(sale_at date, product_id integer references product);

insert into sale(sale_at, product_id) values(sysdate, 1);
insert into sale(sale_at, product_id) values(sysdate, 1);
insert into sale(sale_at, product_id) values(sysdate, 1);
insert into sale(sale_at, product_id) values(sysdate, 2);
insert into sale(sale_at, product_id) values(sysdate, 2);
Run Code Online (Sandbox Code Playgroud)

 

select sale_at, product_id from sale where product_id not in (select parent_id from split)
union all
select sale_at, child_id from sale join split on(parent_id=product_id);

/*
|                     SALE_AT | PRODUCT_ID |
--------------------------------------------
| June, 10 2013 15:18:22+0000 |          2 |
| June, 10 2013 15:18:22+0000 |          2 |
| June, 10 2013 15:18:22+0000 |          3 |
| June, 10 2013 15:18:22+0000 |          2 |
| June, 10 2013 15:18:22+0000 |          3 |
| June, 10 2013 15:18:22+0000 |          2 |
| June, 10 2013 15:18:22+0000 |          3 |
| June, 10 2013 15:18:22+0000 |          2 |
*/
Run Code Online (Sandbox Code Playgroud)

( SQLFiddle )

- - 编辑:

澄清一下,我试图用一条merge语句得到的效果是:

insert into sale(sale_at, product_id)
select sale_at, child_id from sale join split on parent_id=product_id;

delete from sale where product_id in (select parent_id from split);
Run Code Online (Sandbox Code Playgroud)

我想要单个语句溶液中的原因是为了防止竞争状态,其中另一事务的插入/提交该之间的数据insertdelete在主事务。

Vin*_*rat 10

你可以一次性完成,有点:

SQL> MERGE INTO sale s
  2  USING (SELECT ROWID rid, sale_at, product_id
  3           FROM sale
  4          WHERE product_id IN (SELECT parent_id FROM split)
  5         UNION ALL
  6         SELECT CAST (NULL AS ROWID) rid, sale_at, child_id FROM sale
  7           JOIN split ON (parent_id = product_id)) m
  8  ON (s.ROWID = m.rid)
  9  WHEN MATCHED THEN
 10     UPDATE SET s.sale_at = m.sale_at
 11     DELETE WHERE 1 = 1
 12  WHEN NOT MATCHED THEN
 13     INSERT VALUES (m.sale_at, m.product_id);

Done

SQL> select * from sale;

SALE_AT                                  PRODUCT_ID
----------- ---------------------------------------
11/06/2013                                        2
11/06/2013                                        2
11/06/2013                                        3
11/06/2013                                        3
11/06/2013                                        3
11/06/2013                                        2
11/06/2013                                        2
11/06/2013                                        2
Run Code Online (Sandbox Code Playgroud)

这将从销售中删除存在于拆分中的行,并将它们替换为相应的拆分产品。

你也可以这样写:

SQL> MERGE INTO sale s
  2  USING (SELECT CASE WHEN row_number() over (PARTITION BY s.rowid
  3                                             ORDER BY sp.rowid) = 1
  4                     THEN  s.rowid
  5                END rid,
  6                s.sale_at, sp.parent_id, sp.child_id
  7           FROM split sp
  8           JOIN sale s
  9             ON sp.parent_id = s.product_id) m
 10  ON (s.rowid = m.rid)
 11  WHEN MATCHED THEN UPDATE SET s.product_id = m.child_id
 12  WHEN NOT MATCHED THEN INSERT VALUES (m.sale_at, m.child_id);
Run Code Online (Sandbox Code Playgroud)

此处,销售中的行将被其第一个拆分组件替换(更新),并且将插入其他组件。