Saturday, August 24, 2013

Using DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS to see "Are we there yet?"

Oracle has actually made it pretty easy to track things.  The DBMS_APPLICATION_INFO package has some really simple to use procedures to help us see what is happening in our code.

Something that is likely under used is the SET_SESSION_LONGOPS procedure.

Using this in your code and you can really know "Are we there yet?" and more importantly, how close to "there" you are.

The procedure does look a tad confusing when you look at its description and the Oracle docs are a bit thin on it as well.  (In defense of the doc writers, they tend to write more robustly for features that are well used.  Obscure features don't get much press and hence the doc writing will not have the time to focus on writing a verbose description.)

The basic idea with this procedure is to "publish" or "register" your running application into the view V$SESSION_LONGOPS where you can then query that view to see how it's going.

Here is the description of the procedure.

DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS (
   rindex      IN OUT BINARY_INTEGER,
   slno        IN OUT BINARY_INTEGER,
   op_name     IN     VARCHAR2       DEFAULT NULL,
   target      IN     BINARY_INTEGER DEFAULT 0,
   context     IN     BINARY_INTEGER DEFAULT 0,
   sofar       IN     NUMBER         DEFAULT 0,
   totalwork   IN     NUMBER         DEFAULT 0,
   target_desc IN     VARCHAR2       DEFAULT 'unknown target',
   units       IN     VARCHAR2       DEFAULT NULL) 

set_session_longops_nohint constant BINARY_INTEGER := -1;

So what does all that mean?  The rindex and slno values are (as far as we’re concerned) combined into a key used to identify the row we are working with in v$session_longops.  We get a new row by setting the rindex to set_session_longops_nohint, and then the returned values for both will be used in subsequent calls.  

The rest of the parameters are very much up to us to decide what they are.  The op_name is a key field we could use to see the information we want. This corresponds to the column of the same name in v$session_longops.  Of course you could also use other columns like sid and serial# form the view as well.

Let take a look at an example.  This code is just setup to do some mindless work just so it takes some time to complete so we can see something going on. 

In one session I’ll run this code block:

DROP TABLE SSL_DEMO;

CREATE TABLE SSL_DEMO
  (SSL_KEY    NUMBER(8),
   SSL_STRING VARCHAR2(10),
   SSL_DATE   DATE);  

DECLARE
  rindex    BINARY_INTEGER;
  slno      BINARY_INTEGER;
  totalwork number;
  sofar     number;
  obj       BINARY_INTEGER; 
begin
  -- This will get a new row in v$session_long_ops
  rindex := dbms_application_info.set_session_longops_nohint;
  -- Haven't processed any rows sofar
  sofar := 0;
  -- We will process 200,000 when were done
  totalwork := 200000;
  -- Puting the object id into the context parameter
  select object_id into obj from user_objects where object_name = 'SSL_DEMO';
  for i1 in 1..1000 loop
    for i2 in 1..100 loop
      insert into ssl_demo
        values (i2, DBMS_RANDOM.STRING('a', 10), sysdate);
      sofar := sofar + 1;
    end loop;
    for i3 in 1 .. 100 loop
      delete from ssl_demo where ssl_key = i3;
      sofar := sofar + 1;
    end loop;
    -- Here is where we first get a row then update it in v$session_longops
    dbms_application_info.set_session_longops(rindex, slno,
    'RPM SSL DEMO', obj, 0, sofar, totalwork, 'Table Work', 'Rows Processed');
  end loop;
end;
/

Notice that in the block I keep a simple count of the rows as they are inserted and deleted by just incrementing sofar.  Then at the end of each set up inserts and deletes I use set_session_longops to update the view.  While that runs I’ll run this in another session.

select opname, target_desc, elapsed_seconds, time_remaining, (sofar/totalwork)*100 PCT
from V$SESSION_LONGOPS
where opname = 'RPM SSL DEMO';

The output from this query would look something like this (each line is a re-run of the query):

OPNAME         TARGET_DESC    ELAPSED_SECONDS TIME_REMAINING PCT Done
-------------- -------------- --------------- -------------- --------
RPM SSL DEMO   Table Work                4.00          45.00     8.10
RPM SSL DEMO   Table Work               50.00          31.00    61.40
RPM SSL DEMO   Table Work               71.00          18.00    79.50
RPM SSL DEMO   Table Work               88.00           7.00    92.60
RPM SSL DEMO   Table Work               98.00            .00   100.00

The time remaining column isn’t perfect but it’s a pretty good indication of how long it will take to complete.

Selecting the message column from v$session_longops while the block is running would look like this:

select message from V$SESSION_LONGOPS where opname = 'RPM SSL DEMO';
RPM SSL DEMO: Table Work 95705: 73200 out of 200000 Rows Processed done
RPM SSL DEMO: Table Work 95705: 135400 out of 200000 Rows Processed done
RPM SSL DEMO: Table Work 95705: 200000 out of 200000 Rows Processed done

Kinda cool eh?

Monday, August 19, 2013

A Chris Date Master Class - Cleveland October 22nd-24th 2013





Back 1000 years ago when I took my first (and my University’s first) relational database class, the book we used was by Mr. Date.   I still have a copy of that book and refer to it now and then.  If you'd like to hear from one of the founders of relational database systems here is your opportunity.  

He spoke at the Hotsos symposium a few years back and I found the talk to be quite informative.



SQL and Relational Theory: How to Write Accurate SQL
-- A Chris Date Master Class

Chris Date is coming to Cleveland October 22nd-24th to present the course: “SQL and Relational Theory:  How to Write Accurate SQL”.  For less than $600 spend 3 days with CJ Date to see how to solve your problems with relationally accurate techniques.  Don't miss this opportunity learn from one of the founders of relational theory. Invest in yourself to fine tune skills you will need for the rest of your professional life.  Course details: https://www.neooug.org/DateSeminar/ChrisDate_SQL_and_relational_theory.htm

Wednesday, August 7, 2013

A sample from the Hotsos SQL Optimization course

Hey everyone!  I'm in the big league now, I've got my first video out there! (OK you can't see me, but you can hear me!)  It's a sample from the 5 day Hotsos SQL Optimization course.  Great stuff! 

Click HERE to see it.

Have a great day!

Saturday, July 27, 2013

Some Rambles

I'm currently flying at some 36000 feet on my way home and was just thinking about some of the myths that seem to have a very strong life about Oracle.  Here are just a few that I tend to bump into as I teach my classes.

Full Table scans are bad - This one seems to be mostly dead but sometimes I still run across folks that somehow believe that if you see a full table scan in a query, that is the problem.  Hold on there cowboy.  It might be but personally I've seen INDEX scan turn out to be worse then full table scans.  To find one row a full table scan is really always a bad idea but there are plenty of times a full table scan is good.  In a very broad view, if you're going to get touch more then about 20% of the blocks of the table, you need to do a full table scan.

COUNT(1) is better then COUNT(*) - Nope. They do the exact same thing.  This one is so easy to test I can't believe the smart folks I've come across that haven't tested this to see that there is no difference.

You can Pin buffers in the buffer cache - No you can't.  The CACHE option at the table level only changes the behavior of a full table scan.  And this should be used with caution.  For a table of reasonable size Oracle reads the table into the LRU (Least Recently Used) of the LRU list.  This means that a table wouldn't stay in cache very long, likely it will just get cycled out as the scan goes.  Setting the cache option caused it to move the full scan to the MRU (Most Recently Used) end of the list.  This will cause the table to stay in cache LONGER, but not pinned.  Even the old KEEP pool didn't "keep" buffers, it was managed slightly differently but buffers will age out there too.

When you see X step in a plan it's always bad.  Really?  Do you really think the guys and gals that write the code for the optimizer are that stupid?  Seriously they are not going to put an option in to the optimizer that is "always" bad.  Sure some steps might seem to be bad in your environment more then not.  For example the SKIP SCAN on indexes isn't always good, but there are times when it rocks.  Any step in a plan can be "good" or "bad".  It's hard to just look at a plan and say with absolute certainty that a particular step is BAD.  To know for sure you need some run time stats to tell you what happened.  Yes if you know your tables and indexes very well (which I hope you do) you have a much better chance of looking at a plan and gaging the "goodness" or "badness" of a step.