4

I want to know how long my stored procedure is taking to execute from the time an external process hits the database and says execute this to the time the database returns back to the process and says here ya go.

Is there a simple easy way to do this very basic task?

Of course a report of what is happening during the stored procedure execution and how much of the stored procedure's time is spent doing each task (inserts, plsql string manipulation etc.) would be a bonus, but I really just want something simple and easy to use. (And free)

APC
  • 144,005
  • 19
  • 170
  • 281
kralco626
  • 8,456
  • 38
  • 112
  • 169
  • 1
    Have you considered the [dba](http://dba.stackexchange.com) site? This isn't really programming related (as it is phrased anyway, maybe I'm missing something though.) – Mat Oct 05 '11 at 15:42
  • 6
    @Mat - disagree utterly. Profiling the performance of a program is something developers should know how to do. – APC Oct 05 '11 at 15:44
  • @APC: I didn't say that. I said it might be more appropriate on a site dedicated to database professionals. Also not: no close vote - I'm just trying to help here. – Mat Oct 05 '11 at 15:46
  • Well I guess It's not directly programming related, but as a application developer who writes a lot of sql I would like to know this information. When I used to do a lot of MSSql development SSMS gave me more statistics and execution information that I could ever want in one nice easy to use tool. I can't figure out how to get anything close to that in Oracle and it is really bothering me not to know how my procedures are preforming. – kralco626 Oct 05 '11 at 15:52
  • @Mat - "Also not: no close vote" hu? – kralco626 Oct 05 '11 at 15:54
  • 1
    @Mat - okay, but there are a number of database developers on SO. Even if we are outnumbered by the C# guys :( – APC Oct 05 '11 at 15:56
  • @kralco626: there was no close vote when I posted. There is one now (reason selected: off topic) (still not from me though) – Mat Oct 05 '11 at 15:57
  • @APC - I'm also a little opposed to having so many different sites for similar things. I mean there are a lot of aspects of DBA work that is more closely related to sql development than sql development is related to... say html – kralco626 Oct 05 '11 at 15:59
  • @Mat - ooo I was just confused at what you were trying to say. Gotcha – kralco626 Oct 05 '11 at 15:59

3 Answers3

6

If you're using Oracle 11g you should have a look at the hierarchical profiler, DBMS_HPROF. This is a tool which will give you elapsed timings for all the nodes in a PL/SQL program. As the name suggests, it is especially useful for investigating programs which call programs which call programs. It also identifies timing for SQL statements distinct from function calls. Find out more.

It is part of the standard 11g deployment, and hence is free. Well, once you've paid for your license it's free :)

By default rights on the DBMS_HPROF package are not granted to any one. So, you'll need to get somebody with SYSDBA access to see you right. The analysis also requires the creation of some tables. Neither of these things should be an issue but I know sometimes they are.


Alas, you're on an earlier version. So that leaves you with just DBMS_PROFILER, which has been around since 9i. This basically works well for a single program. Its main drawback is that we can only use it on programs for which we have the CREATE privilege (i.e. just programs in our schema unless we have the CREATE ANY PROCEDURE privilge, which usually means being a DBA). Also, for profiling embedded SQL statements we need to use the DBMS_TRACE package. Find out more.

APC
  • 144,005
  • 19
  • 170
  • 281
  • well the problem there is that our development and test level databases are not yet upgraded to 11g. – kralco626 Oct 05 '11 at 15:56
  • @kralco626 - software version is the sort of information you ought to include in your question. – APC Oct 05 '11 at 16:24
3

SIMPLEST,CHEAPEST OPTION You could log events to a "debug" table and check the duration between the logs, like for ex. Log event before insert into table a with time stamp a into debug table. Log event after insert into table a with timestamp b into debug table.

It's a bit time consuming if you're stored procedure is anything more than 5 lines, but it will help you none the less. Also implement a debug flag, so when you need to log time stamps, set the flag at the top of the procedure to true, and then re-compile it. When executing the stored procedure, it will then go and log all your events, when done with debugging, set the flag to false and re-compile.

  • This does not give me the end to end performance; but rather, at best, only the time it takes from the begin to the end statement. – kralco626 Oct 05 '11 at 16:07
  • 1
    +1 Using a log table (autonomous transaction) can be done at ANY point in your programs. Wrap in a package and simply call it where you like. You can track timings at various points in the processing via timestamps in the log table. Add other info besides a simple message if you like (calling function, context, etc). – tbone Oct 05 '11 at 18:28
  • Also, nice thing about log tables (using autonomous transactions) is you can tail the log of a long running process by querying the log table as the process is running. I believe the profiler is an after-the-fact setup, so you get your info when you stop profiling. I guess I just like the flexibility of good ole log tables. – tbone Oct 05 '11 at 18:42
  • I disagree that this is the simplest or cheapest option. Anything which requires us to change our code is not cheap, especially if we're building an infrastructure which needs to be deployed into production along with our actual code. (Obviously we already have a logging infrastructure this objection is moot but I suspect it doesn't apply here.) Also, a log table doesn't easily show us which events, let alone which lines, are taking the most time. I'm a big fan of instrumentation and logging, but they don't help with profiling our code. – APC May 28 '19 at 13:26
0

If you are looking to write your own time lapse into your PL/SQL programs the below is my template code for doing this. Also, this requires no assistance from your DBA. My DBA's are often reluctant to give me more access to more things. That, and I hate waiting for them to actually give me access.

/* This procedure is simply an example of parsing out an Elapsed time into their individual time parts */
/*   In other words it provides to the user the elapsed time in hours : minutes : seconds . milliseconds */

DECLARE

  G_START_TIME1     NUMBER        := DBMS_UTILITY.GET_TIME;
  G_START_TIME2     NUMBER        := DBMS_UTILITY.GET_TIME;
  vG_ELAPSED_TIME   VARCHAR2(200);

  /* Variables below are used for storing Elapsed time. */
  RETVAL           NUMBER;
  hourss            NUMBER;
  minutess          NUMBER;
  secondss          NUMBER;
  millisecondss     NUMBER;
    n_hrs         NUMBER := 360000;
    n_mins        NUMBER := 6000;
    n_secs        NUMBER := 100;
    n_sixty       NUMBER := 60;
    n_thirty      NUMBER := 30;
    n_ten         NUMBER := 10;

  v_PrintTime     VARCHAR2(200);


  /* The procedure below is simply used to simulate the passage of time */
  PROCEDURE waste_time(pn_Seconds NUMBER) IS

  n_CentiSeconds  NUMBER  :=  pn_Seconds * 100;
  n_ProgramStart  NUMBER  := DBMS_UTILITY.GET_TIME; /* dbms_utility_get_time returns times in hundreds of a second */

  BEGIN
    WHILE DBMS_UTILITY.GET_TIME < n_ProgramStart + n_CentiSeconds
    LOOP
      null;
    END LOOP;
  END waste_time;

BEGIN

  G_START_TIME1   :=    -2019618227; --dbms_utility.get_time;

  --waste_time(1);
  --dbms_output.put_line('1 Second Wasted'|| chr(10));

  G_START_TIME2   :=    G_START_TIME1 + 366110; ---2019619227; --dbms_utility.get_time;

  dbms_output.put_line('Start_time: '||G_START_TIME1);
  dbms_output.put_line('End_time:   '||G_START_TIME2||chr(10));

  vG_ELAPSED_TIME :=  G_START_TIME2 - G_START_TIME1;
  millisecondss   :=  vG_ELAPSED_TIME * n_ten;
  secondss        :=  vG_ELAPSED_TIME / n_secs;
  minutess        :=  vG_ELAPSED_TIME / n_mins;
  hourss          :=  vG_ELAPSED_TIME / n_hrs;

  dbms_output.put_line('Total Time: '||millisecondss ||' Milliseconds');
  dbms_output.put_line('Total Time: '||vG_ELAPSED_TIME ||' Centiseconds');
  dbms_output.put_line('Total Time: '||secondss ||' Seconds');
  dbms_output.put_line('Total Time: '||minutess ||' Minutes');
  dbms_output.put_line('Total Time: '||hourss ||' Hours');

  millisecondss   :=  mod(vG_ELAPSED_TIME * n_ten, 1000);            /* Milliseconds into Seconds.  Returns left over millisedonds */
  secondss        :=  trunc(mod(vG_ELAPSED_TIME / n_secs, n_sixty)); /* Seconds into hours.  Returns how many seconds are left over */
  minutess        :=  trunc(mod(vG_ELAPSED_TIME / n_mins, n_sixty)); /* Seconds into hours.  Returns how many seconds are left over */
  hourss          :=  trunc(vG_ELAPSED_TIME / n_hrs);                /* Spit out hours only using just trunc here.  Since we don't need to build up Days we can leave this as is */

  v_PrintTime     :=  hourss ||':'|| minutess ||':'|| secondss ||'.'|| millisecondss;

  dbms_output.put_line(chr(10) ||'Elapsed Time: '|| v_PrintTime);


END;
Code Novice
  • 2,043
  • 1
  • 20
  • 44