Skip to Main Content
  • Questions
  • How to count records that are not yet commited in a session/transaction?

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Dave.

Asked: April 11, 2012 - 1:57 pm UTC

Last updated: April 16, 2012 - 4:20 pm UTC

Version: 10.2.0.4

Viewed 10K+ times! This question is

You Asked

Hi Tom,

How do I count the number of records inserted/updated/deleted that have not yet been commited in a session?

Development has a package with some bugs where they were committing(do to the logic flow). We are trying to make an alert so that we will get notified if we say set the condition of no commmit has been issued and the total records exceedes say 150,000.

Thanks,
Dave

and Tom said...

we do not internally track the number of records modified by a transaction.

Perhaps you could approach this differently. Use the resource manager to limit the amount of UNDO any transaction can generate. Assign this plan to the user running the package.

http://docs.oracle.com/cd/E11882_01/server.112/e25494/undo005.htm#ADMIN11477

You would determine what the maximum undo a transaction can generate (instead of "rows" modified) and use that to limit the size of a transaction.

Rating

  (4 ratings)

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

Comments

Dave Giblin, April 12, 2012 - 8:56 am UTC


why not trigger

A reader, April 13, 2012 - 3:35 am UTC

Hi Tom,

Why do not you suggest use trigger to solve this?

I know you are not a big fan of trigger, but trigger for this requirement might be more accurate than utilize 'resource limit'.

Simple idea is use a temporary table to keep the corresponding DML and impacted rows.

Any hole or objection to my below proposale?

I know 1 big issue is we need to create trigger for all tables.
So comes a question for you, do we have DML functions schema or even database wise? Something like below:

create or replace trigger magic_trigger
after insert or update or delete
-- or even 'after DML on database'
begin
dbms_output.put_line(ora_action || ' ' ||ora_obj || ' with ' ||ora_rows_affected || ' rows');
-- give us something like
-- 'update scott.test with 3 rows'
-- 'delete reporting.test with 4 rows'
-- 'insert hr.employee with 10 rows'
end;


create global temporary table session_info(sid number, dml_type varchar2(10), cnt number);
create table test(id int, name varchar2(10));
create or replace trigger trg
after insert or update or delete on test
declare
v_action varchar2(100);
begin
if updating then
v_action:='update';
elsif deleting then
v_action:='delete';
elsif inserting then
v_action:='insert';
end if;

-- i know below does not work, but how?
v_number_of_rows_affected := sql%rowcount;

merge into session_info a
using (select sys_context('userenv','sid') sid, v_action dml_type, v_number_of_rows_affected cnt from dual) b
on (a.sid = b.sid and a.dml_type = b.dml_type)
when matched then
update set a.cnt = a.cnt + b.cnt
when not matched then
insert values(b.sid, b.dml_type, b.cnt);
end;
/
Tom Kyte
April 13, 2012 - 1:09 pm UTC

Why do not you suggest use trigger to solve this?


because I never lead with triggers - ever - never.

http://www.oracle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html


Let me list the deficiencies in even considering a trigger for this:

a) how would this practically work given they wanted:

How do I count the number of records inserted/updated/deleted that have not yet been committed in a session?


that means for every table touched, in every schema they might touch a table. not just 'their' schema, any schema, every schema. The code changes over time - it might use dynamic sql - you don't have the exhaustive list of all tables for all times.

b) where is your logic to reset any counter to zero after they commit? (I can show you such logic, but it adds even more overhead to the entire bad idea)

c) every one is penalized performance wise - in a big way.

d) insert /*+ APPEND */ will cease working in the existence of a trigger

e) the massive performance overhead - they are updating lots of stuff.


ops$tkyte%ORA11GR2> create table t1 ( x int primary key, y varchar2(30) );

Table created.

ops$tkyte%ORA11GR2> create table t2 ( x int primary key, y varchar2(30) );

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> insert /*+ append */ into t1
  2  select rownum, rownum
  3    from dual
  4  connect by level <= 150000;

150000 rows created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> insert /*+ append */ into t2
  2  select rownum, rownum
  3    from dual
  4  connect by level <= 150000;

150000 rows created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> create or replace package my_pkg
  2  as
  3          g_cnt number := 0;
  4  end;
  5  /

Package created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> create or replace trigger t2_aiudfer
  2  after insert or update or delete on t2 for each row
  3  begin
  4          my_pkg.g_cnt := my_pkg.g_cnt+1;
  5  end;
  6  /

Trigger created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2          l_start_cpu number := dbms_utility.get_cpu_time;
  3  begin
  4          for i in 1 .. 150000
  5          loop
  6                  update t1 set y = lower(y) where x = i;
  7          end loop;
  8          dbms_output.put_line( 'cpu hsecs = ' || ( dbms_utility.get_cpu_time-l_start_cpu ) );
  9  end;
 10  /
cpu hsecs = 832

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2          l_start_cpu number := dbms_utility.get_cpu_time;
  3  begin
  4          for i in 1 .. 150000
  5          loop
  6                  update t2 set y = lower(y) where x = i;
  7          end loop;
  8          dbms_output.put_line( 'cpu hsecs = ' || ( dbms_utility.get_cpu_time-l_start_cpu ) );
  9  end;
 10  /
cpu hsecs = 1133

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> exec dbms_output.put_line( my_pkg.g_cnt );
150000

PL/SQL procedure successfully completed.



I know I wouldn't want that overhead - and think about it - it would be for EVERYTHING - not just this one package.


f) and even if you had such a counter - so what, what could an external session do about it? You'd have to pop that counter into an application context or v$session (yet MORE large overhead as your counting, trigger runtime is going up and up and up)

g) And most of all - because counting records is a bad approach to the underlying problem. they are trying to contain the use of certain resources (almost certainly the undo space being used). Controlling this by the resource - not by a rowcount - is much more appropriate.

Think about it - I could generate orders of magnitude more undo updating 150,000 rows than you might or vice versa. There is no true relationship between the number of rows modified and the use of some resource. It would be wholly dependent on the nature of the modifications - NOT the number of modifications.

HSC Heap Segment Block Changes?

Matthew McPeak, April 16, 2012 - 1:12 pm UTC

Tom,

Can you explain why statistic "HSC Heap Segment Block Changes" in 11g2 cannot be helpful here?

At first glance, it seems to fit the bill, but I cannot find it in the reference guide.

Thanks,
Matt

Tom Kyte
April 16, 2012 - 4:20 pm UTC

let me turn it around,

why don't you show how it would be?


first - it is blocks, not rows. 1000 blocks might be 100,000 rows (or more) or 1000 rows

second - it is cumulative for a session - like most all statistics, it doesn't resent upon comit

third - assume you solve the above two - what then? would you run a continuous process that monitored that stat by session - that would be huge overhead.


I'll stick with "if you want to limit the size of a transaction, go after undo generated - that is THE indication of how big the transaction is - rows is not."

Nevermind...

Matthew McPeak, April 16, 2012 - 1:22 pm UTC

Did my "first glance" using a really fat table that only gets 1 row per block. Nevermind.
Tom Kyte
April 16, 2012 - 4:20 pm UTC

;)

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library