目的
使用MERGE語句從一個或多個數據源中選擇數據插入到一個表或視圖中。你可以指定條件,以確定是否更新或插入到目標表或視圖。
MERGE語句是一種方便結合多個操作的方式,它可以讓你避免多個INSERT,UPDATE和DELETEDML語句。
MERGE是一種確定性的語句,在同一個MERGE語句中不能多次更新目標表的同一行。
前提
你必須有目標表的INSERT和UPDATE對象權限和源表的SELECT對象權限。要指定merge_update_clause的DELETE從句,也必須要有目標表的DELETE對象權限。
語法
MERGE [ hint ]
INTO [ schema. ] { table | view } [ t_alias ]
USING { [ schema. ] { table | view }
| subquery
} [ t_alias ]
ON ( condition )
[ merge_update_clause ]
[ merge_insert_clause ]
[ error_logging_clause ] ;
注意:雖然這個SQL語句有UPDATE、INSERT和LOGGING從句,但是只能有一個分號(;)。
-
merge_update_clause從句
WHEN MATCHED THEN
UPDATE SET column = { expr | DEFAULT }
[, column = { expr | DEFAULT } ]...
[ where_clause ]
[ DELETE where_clause ]
-
merge_inser_clause
WHEN NOT MATCHED THEN
INSERT [ (column [, column ]...) ]
VALUES ({ expr [, expr ]... | DEFAULT })
[ where_clause ]
-
error_logging_clause
LOG ERRORS
[ INTO [schema.] table ]
[ (simple_expression) ]
[ REJECT LIMIT { integer | UNLIMITED } ]
語義
INTO 子句
使用into子句指定要插入和更新的目標表或視圖。
USING 子句
使用using子句指定插入和更新的數據源。數據源可以一張表、視圖或是子查詢。
ON子句
使用on子句指定merge操作條件是插入或更新。對於目標表的搜索條件爲真時,Oracle數據庫從源表獲取數據更新到目標表的相應行。如果條件不成立的任意行,則數據庫插入基於來源表相應的行到目標表。
merge_update_clause
merge_update_clause
指定目標表的新列值。如果ON子句的條件爲真,Oracle執行更新。如果update子句被執行,那麼所有目標表上定義的更新觸發器都被激活。
如果你想數據庫只有指定條件爲真時才執行更新操作,則需要指定 where_clause
。條件可以參考數據源或目標表。如果條件爲假,則合併表時數據庫跳過更新操作。
指定 DELETE
where_clause
刪除表中正在填充或更新的數據。該從句隻影響目標表中被合併操作更新的行。DELETE
WHERE條件求被UPDATE SET ... WHERE計算後的更新值,而不是原始值。如果目標表的行滿足DELETE條件,但不包含在ON子句中所定義的條件,該行是不被刪除的。定義在目標表上的刪除觸發器在刪除每一行後都會被觸發。
merge_insert_clause
如果ON子句的條件爲假, merge_insert_clause
指定值插入到目標表的列上。如果INSERT子句被執行,那麼定義在目標表上的所有插入觸發器被激活。如果省略了INSERT關鍵字後列的列表,則目標的列數必須與VALUES子句中值的列數匹配。
爲了將來源表的所有行都插入目標表,可以在ON子句中使用常量過濾條件。一個常量過濾條件的例子是ON(1=0)。Oracle承認這樣一個常量條件,並無條件插入源表所有行到目標表。這種方法是不同於省略 merge_update_clause,
在這樣情況下,數據庫仍然執行關聯。使用常量過濾條件,沒有關聯被執行。
如果想要數據執行插入操作僅僅是指定條件爲真時,則需要指定 where_clause
。該條件只能是參考數據源表。Oracle數據庫跳過所有行條件爲假的插入操作。
error_logging_clause
在合併語句中error_logging_clause具有與INSERT語句相同的行爲。
Examples
創建員工表emp,部門表depart和獎金錶bonuses。
創建員工表
create table emp(
employee_id number,
employee_code varchar2(30),
employee_name varchar2(50),
brithdate date,
depart_id number);
insert into emp values (1,'0001','KIT',date'1996-10-18',1);
insert into emp values (2,'0002','張三',date'2013-10-3',1);
insert into emp values (3,'0003','李四',date'2003-11-23',2);
insert into emp values (4,'0004','王五',date'2001-1-06',2);
insert into emp values (5,'0005','小六',date'2011-09-08',3);
創建部門表
create table depart (
depart_id number,
depart_code varchar2(30),
depart_name varchar2(50));
insert into depart values (1,'10','市場部');
insert into depart values (2,'20','研發部');
insert into depart values (3,'30','拓展部');
insert into depart values (4,'40','後勤部');
創建獎金錶create table bonuses (
employee_id number,
bonuses number not null);
創建日誌表
create table logs(
log_id number,
log_message varchar2(200));
1.按照部門的發放獎金,市場部100,研發部200,拓展部200,後勤部80。使用merge語句插入各部門員工的獎金。
merge into bonuses b
using (select e.employee_id, e.employee_name, d.depart_id, d.depart_code
from emp e, depart d
where e.depart_id = d.depart_id) a
on (b.employee_id = a.employee_id)
when not matched then
insert
(b.employee_id, b.bonuses)
values
(a.employee_id,
decode(a.depart_code, '10', 100, '20', 200, '30', 200, '40', 80));
查詢獎金錶
SQL> select * from bonuses;
EMPLOYEE_ID BONUSES
----------- ----------
5 200
4 200
3 200
1 100
2 100
2.市場不所有員工,在原來獎金的基礎上加20%。
merge into bonuses b
using (select e.employee_id, e.employee_name, d.depart_id, d.depart_code
from emp e, depart d
where e.depart_id = d.depart_id
and d.depart_code = '10') a
on (b.employee_id = a.employee_id)
when matched then
update set b.bonuses = b.bonuses + b.bonuses * 0.2;
查詢獎金錶
SQL> select *from bonuses;
EMPLOYEE_ID BONUSES
----------- ----------
5 200
4 200
3 200
1 120
2 120
市場部的員工的獎金有原來的100變成了120。
3.刪除研發部員工張三的獎金,其他員工獎金減少10%。
merge into bonuses b
using (select e.employee_id, e.employee_name, d.depart_id, d.depart_code
from emp e, depart d
where e.depart_id = d.depart_id
and d.depart_code = '20') a
on (b.employee_id = a.employee_id)
when matched then
update
set b.bonuses = b.bonuses - b.bonuses * 0.1
delete where b.employee_id = 3;
查詢獎金:
SQL> select *from bonuses;
EMPLOYEE_ID BONUSES
----------- ----------
5 200
4 180
1 120
2 120
研發部現在只有李四有獎金,由原來的200變爲180.。
注意:使用DELETE子句有以下條件限制。
a.刪除記錄比較在目標表中存在。
b.必須滿足On子句的條件。
c.刪除的記錄必須包含update set ...子句中。
4.研發部門所有有獎金的員工,獎金加30%,沒有獎金的員工加200.
merge into bonuses b
using (select e.employee_id, e.employee_name, d.depart_id, d.depart_code
from emp e, depart d
where e.depart_id = d.depart_id
and d.depart_code = '20') a
on (b.employee_id = a.employee_id)
when matched then
update set b.bonuses = b.bonuses + b.bonuses * 0.3
when not matched then
insert (b.employee_id, b.bonuses) values (a.employee_id, 200);
查詢獎金錶
SQL> select *from bonuses;
EMPLOYEE_ID BONUSES
----------- ----------
5 200
4 234
3 200
1 120
2 120
研發部張三原來沒有獎金,現在爲200。李四由180變成234.
5.創建創建錯誤日誌表
SQL> EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG('bonuses', 'errlog');
將錯誤日誌表與獎金關聯在一起,這樣在做獎金錶錯誤時。如果發生錯誤,就會將錯誤信息記錄到日誌表。
例如,將拓展部員工的獎金置空。
merge into bonuses b
using (select e.employee_id, e.employee_name, d.depart_id, d.depart_code
from emp e, depart d
where e.depart_id = d.depart_id
and d.depart_code = '30') a
on (b.employee_id = a.employee_id)
when matched then
update set b.bonuses = null
log errors into errlog('bonuses_error') reject limit 20;
查看獎金錶
SQL> select *from bonuses;
EMPLOYEE_ID BONUSES
----------- ----------
5 200
4 234
3 200
1 120
2 120
發現拓展部的員工小劉的獎金還是200,並沒有置空。
查看錯誤日誌表
SQL> select ORA_ERR_NUMBER$,ORA_ERR_MESG$ from errlog;
ORA_ERR_NUMBER$ ORA_ERR_MESG$
--------------- --------------------------------------------------------------------------------
1407 ORA-01407: 無法更新 ("GDSHEC"."BONUSES"."BONUSES") 爲 NULL