Skip to Main Content
  • Questions
  • Caputre number of inserts and update counts in MERGE statement

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, satish.

Asked: February 25, 2005 - 8:21 pm UTC

Last updated: September 09, 2010 - 8:47 pm UTC

Version: 8.1.6

Viewed 10K+ times! This question is

You Asked

In 9iR1, Oralce released a new statement called MERGE (a.k.a. UPSERT), Is it possible to capture the total number of inserts and updates seperately in MERGE statements.

In previous version, we used to do this by
FOR x IN (SELECT empno, ename, job, deptno FROM tmp_Emp)
LOOP
<<MyBlock>>
BEGIN
INSERT INTO emp (empno, ename, job, deptno) VALUES (x.empno, x.ename, x.job, x.deptno) ;
l_TotalInsertCount := l_TotalInsertCount + 1 ;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
UPDATE emp SET ename = x.ename, job =x.job, deptno = x.deptno WHERE empno = x.empno ;
l_TotalUpdateCount := l_TotalUpdateCount ;
END MyBlock;
END LOOP;

How can we do the same on MERGE Statements?

All Merge statement provide is that, "x" records merged.

Is there a work around for this?

Regards,
Satish


and Tom said...

There is nothing to "workaround"

MERGE is a sql statement that has a job to do. it reports back what it did, just like DELETE, INSERT and UPDATE do.

What we all need to do is change what we expect printed on the bottom of the report, instead of seeing:


Elapsed Time: 5 hours
Rows inserted: 12
Rows updated: 24


We'll see in the future

Elapsed Time: 5 seconds
Rows processed: 36


You will have to decide if the number 36 and 5 seconds is sufficient or if you need to expend the huge resources necessary to find out 12 and 24.

Rating

  (7 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Workaround

Satish, February 26, 2005 - 9:59 am UTC

In our current requirement, we capture the total number of updates and inserts for our application. When ever we run the ETL job, we capture insert and update details. But by using merge, we are unable to capture those details. but the requirement specs, says, we need to capture those details.. hence my question.

I have noticed that MERGE statement is faster compared to the loop we use (FOR x IN (SELECT * FROM Emp)).

regards,
satish


Tom Kyte
February 26, 2005 - 10:11 am UTC

maybe you need to change the requirements to meet the technology?

I find this 'requirement' is generally the result of "we've always done it in the past -- so lets continue doing it"

if not, if you cannot realign your requirements with the technology and what it can do -- your requirements mandate "thou must do it the slow by slow way" -- actually, you can accomplish merge in TWO statements:


update ( a join of TGT with NEW );
insert into TGT select * from NEW where key not in ( select key from TGT);

so you can do much better. but if you want to use merge, you get "rows merged" as that is what merge actually does (don't even think about trying to count it with triggers -- will *not* work)

update a join view

Nopparat V., February 26, 2005 - 10:56 am UTC

I knew that we have at least two ways to update the previous question. The first is to update a join view and the second is to use a correlate query.

update
(select ....
from table_a, table_b
where (join condition))
set column_in_a = column_in_b

or

update table_a
set column_in_a =
(select column_in_b
from table_b
where (join condition)
where exists (select ...)

Could you explain how we can choose these two choices?

Thank you

Tom Kyte
February 26, 2005 - 1:43 pm UTC

update the join if at all feasible.

in 10g, you can always use merge to update the join (as you need not have an "insert" in merge)

so, as long as the primary/unique key is in place in the table_b, update the join.



Trigger

satish, February 26, 2005 - 1:48 pm UTC

I used search facility from your site to find, about triggers and realised that, it won't be feasiable.

Is it possible, oralce would provide the number of inserts or updates in future relase.

regards,
satish

Tom Kyte
February 26, 2005 - 3:02 pm UTC

You would have to file an enhancement request for this, that is done via support.




Merge INSERT, UPDATE Counts

Bijay Pusty, April 21, 2008 - 10:38 am UTC

--1 Compile the ETL PKG
--2 SAVE the RUN_MRG to a .SQL FILE
--3 Do the Data Settings and then Run
-----------------------------------------------------------------------------------------------------
--------------------------------------- START OF ETL PKG --------------------------------------------
-----------------------------------------------------------------------------------------------------
CREATE OR REPLACE PACKAGE etl AS

c_inserting CONSTANT PLS_INTEGER := 0;
c_updating CONSTANT PLS_INTEGER := 1;

FUNCTION merge_counter (
action_in IN PLS_INTEGER DEFAULT c_inserting
) RETURN PLS_INTEGER;

FUNCTION get_merge_update_count RETURN PLS_INTEGER;

FUNCTION get_merge_update_count (
merge_count_in IN PLS_INTEGER
) RETURN PLS_INTEGER;

FUNCTION get_merge_insert_count RETURN PLS_INTEGER;

FUNCTION get_merge_insert_count (
merge_count_in in PLS_INTEGER
) RETURN PLS_INTEGER;

PROCEDURE reset_counters;

END etl;
/

CREATE OR REPLACE PACKAGE BODY etl AS

g_update_counter PLS_INTEGER NOT NULL := 0;
g_insert_counter PLS_INTEGER NOT NULL := 0;

/*-------------- START OF FUNCTION merge_counter --------------------*/
FUNCTION merge_counter (
action_in IN PLS_INTEGER DEFAULT c_inserting
) RETURN PLS_INTEGER IS
BEGIN
CASE action_in
WHEN c_updating
THEN g_update_counter := g_update_counter + 1;
WHEN c_inserting
THEN g_insert_counter := g_insert_counter + 1;
ELSE
RAISE PROGRAM_ERROR;
END CASE;
RETURN 0;
END merge_counter;

/*----------- START OF FUNCTION get_merge_update_count V1 ---------------*/
FUNCTION get_merge_update_count
RETURN PLS_INTEGER is
BEGIN
RETURN g_update_counter;
END get_merge_update_count;

/*----------- START OF FUNCTION get_merge_update_count V2 ---------------*/
FUNCTION get_merge_update_count (
merge_count_in IN PLS_INTEGER
) RETURN PLS_INTEGER IS
BEGIN
RETURN NVL( merge_count_in - g_insert_counter, 0 );
END get_merge_update_count;

/*----------- START OF FUNCTION get_merge_insert_count V1 ---------------*/
FUNCTION get_merge_insert_count
RETURN PLS_INTEGER IS
BEGIN
RETURN g_insert_counter;
END get_merge_insert_count;

/*----------- START OF FUNCTION get_merge_insert_count V2 ---------------*/
FUNCTION get_merge_insert_count (
merge_count_in IN PLS_INTEGER
) RETURN PLS_INTEGER IS
BEGIN
RETURN NVL( merge_count_in - g_update_counter, 0 );
END get_merge_insert_count;

/*-------------- START OF FUNCTION reset_counters --------------------*/
PROCEDURE reset_counters IS
BEGIN
g_update_counter := 0;
g_insert_counter := 0;
END reset_counters;

END etl;
/

-----------------------------------------------------------------------------------------------------
--------------------------------------- END OF ETL PKG ----------------------------------------------
-----------------------------------------------------------------------------------------------------
--
--
--
-----------------------------------------------------------------------------------------------------
--------------------------------------- START OF DATA RUN_MRG ---------------------------------------
-----------------------------------------------------------------------------------------------------
set serverout on
set echo on
set pagesize 100
--
-- Run merge...
--
begin

etl.reset_counters;

merge into target tgt
using source src
on (src.id = tgt.id)
when matched then
update
set value = (case etl.merge_counter(etl.c_updating)
when 0
then src.value
end)
when not matched then
insert
( tgt.id
, tgt.value )
values
( case etl.merge_counter(etl.c_inserting)
when 0
then src.id
end
, src.value );

/* Use update count... */
dbms_output.put_line(sql%rowcount || ' rows merged.');
dbms_output.put_line(etl.get_merge_update_count || ' rows updated.');
dbms_output.put_line(etl.get_merge_insert_count(sql%rowcount) || ' rows inserted.');

/* Use insert count... */
dbms_output.put_line(etl.get_merge_update_count(sql%rowcount) || ' rows updated.');
dbms_output.put_line(etl.get_merge_insert_count || ' rows inserted.');

end;
/

-----------------------------------------------------------------------------------------------------
--------------------------------------- END OF DATA RUN_MRG -----------------------------------------
-----------------------------------------------------------------------------------------------------

--
--
--
-----------------------------------------------------------------------------------------------------
--------------------------------------- START OF DATA SETTINGs --------------------------------------
-----------------------------------------------------------------------------------------------------

spool mg
drop table source;
drop table target;
--
-- Setup source and target tables...
--
create table source ( id int, value varchar2(1) ) tablespace small_data;
create table target ( id int, value varchar2(1) ) tablespace small_data;

insert into source select rownum, substr(object_type,1,1) from user_objects where rownum <= 15;
insert into target select * from source where rownum <= 10;
commit;

-----------------------------------------------------------------------------------------------------
--------------------------------------- END OF DATA SETTINGs ----------------------------------------
-----------------------------------------------------------------------------------------------------


select * from source;
select * from target;
--

select * from source
minus
select * from target;
--
select * from target
minus
select * from source;
--
-- Notice the Difference between two

-- Run the RUN_MRG to MERGE and SEE the RESULTs
@run_mrg


select * from source
minus
select * from target;
--
select * from target
minus
select * from source;
--
---

-- Rollback to Duplicate some source data before Merge
-- this doesn't work becase the source has some dups
-- unable to get a stable set of rows in the source tables ERROR
--
Rollback;
insert into source select id, lower(value) from source where rownum <= 5;
@run_mrg
----




--
-- Rollback and Duplicate some target data & Then MERGE
--
Rollback;
insert into target select id, lower(value) from target where rownum <= 5;

select * from source
minus
select * from target;
--
select * from target
minus
select * from source;
--
@run_mrg
--


select * from source
minus
select * from target;
--
select * from target
minus
select * from source;
--
commit;

drop table source;
drop table target;

spool off
set echo off
Tom Kyte
April 23, 2008 - 5:10 pm UTC

if your goal is to have the slowest possible merge and to be very questionable as to the results (you seem to think we'll call your function "some number of times", we don't have to, we can change over time, we can call it more then you think, less than you think - anytime we want)

then, this would be OK.


Merge Vs Full Outer Join.

Snehasish Das, August 27, 2010 - 9:03 am UTC

Hi Tom,

I came across a interesting substitution to merge statement and just wanted to know your views on it.

we have one fact say TAB1 and one daily incremental table say TAB2. We used to merge the data of TAB2 to TAB1.
Our TA suggested instead of doing a Merge on tables we use a NVL(tab2.col1,tab1.col2) and similiar on other columns and do a full outer join on the tables TAB1 and TAB2. This way we get the inserts as well as the updates as we do a NVL with the first parameter of NVL as the column of incremental table. There are only 8 columns in the table.


Can you please comment of this approach.


Regards,
Snehasish Das,

Tom Kyte
September 07, 2010 - 7:49 am UTC

you do not give an example...

I did not follow your explanation...

I don't know what a "TA" is (teaching assistant??)

merge already gives you INSERTS as well as UPDATES - I don't see how you can replace a MERGE (modifies) with a FULL OUTER JOIN (retrieves)

so, I cannot comment because as it stands here - it doesn't compute.

sql%rowcount

A reader, September 08, 2010 - 3:23 pm UTC

Right after a MERGE statement in a PL/SQL block...

1) Will SQL%ROWCOUNT return the number of rows affected, just like after UPDATE, DELETE and INSERT ?

2) Are there other bluit-in counters like SQL%ROWCOUNT specifically for INSERTS, UPDATES and DELETES within a MERGE ?

Thanks Tom =)
Tom Kyte
September 09, 2010 - 8:47 pm UTC

1) yes.

2) no.

sql%rowcount

A reader, September 08, 2010 - 3:30 pm UTC

(cont'd) I'm using 10.2 for the question right above.
Tom Kyte
September 09, 2010 - 8:47 pm UTC

all versions, the same.