Systems Engineering and RDBMS

Archive for the ‘Oracle’ Category

listagg() – new analytic function in Oracle 11g R2

Posted by decipherinfosys on October 20, 2009

We all have used different methods in order to get a concatenated list of values from a column – pivoting it out, using SYS_CONNECT_BY_PATH function which was introduced in Oracle 10g R1, writing our own functions to do it etc..  In SQL Server also, using XML PATH, one can do it easily as illustrated in some of our posts before – one of them is here.

Oracle 11gR2 now has a new analytic function called listagg() for doing the list aggregation.  So, now all that we need to specify is the name of the column and the separator string that will separate the different values.  Here is the link to the 11gR2 documentation on this wonderful function along with some examples to help illustrate it’s usefulness:

http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/functions087.htm

Posted in Oracle, SQL Server | Leave a Comment »

Splitting a number of rows into equal groups

Posted by decipherinfosys on October 16, 2009

At the client site yesterday, one of the developers asked this question: “I want to take the large set of the data that I have in my gigantic table and split it up into balanced non-overlapping sets.  I want the starting value in the set, the ending value and the number of records in that group.  I need this to do my data processing logic for starting parallel concurrent processing of those large data sets.  What is the best way to achieve this?

This client site uses both Oracle and SQL Server so the solution had to work with both the RDBMS.  Luckily enough, there is an analytic function in both which does this very easily for us.  The function that we are talking about is NTILE().  Let’s take this up with an example:

SQL Server:

Let’s say that I want to split all the objects in sys.objects into say 10 different non-overlapping sets.  And then as per the requirement, I need to list out the starting value in the set, the ending value and the total count of the records in the set.  Here is a simple code sample that will do that:

select
min(object_id) as Starting_Value
,    max(object_id) as Ending_Value
,    count(*)       as Total_Records
,    grp_nbr           as Group_Nbr
from
(
select object_id,
ntile(10) over (order by object_id) grp_nbr
from SYS.OBJECTS
) AS IV
group by grp_nbr;

What I get as the output in my test database is this:

Starting_Value Ending_Value Total_Records Group_Nbr
-------------- ------------ ------------- --------------------
4              125243501    123           1
128719511      366624349    123           2
367340373      571865104    123           3
574625090      767341798    122           4
768721791      971150505    122           5
971866529      1163151189   122           6
1165247206     1403152044   122           7
1406628054     1707153127   122           8
1709249144     1941581955   122           9
1943677972     2144726693   122           10

Now, you will notice that not all sets have equal records but they are pretty close.  That is by definition of the NTILE() function.  Here is what BOL states:

If the number of rows in a partition is not divisible by expression, this will cause groups of two sizes that differ by one member. Larger groups come before smaller groups in the order specified by the OVER clause. For example if the total number of rows is 53 and the number of buckets is five, the first three buckets will have 11 rows and the two remaining buckets will have 10 rows each. If on the other hand the total number of rows is divisible by the number of buckets, the rows will be distributed evenly among the buckets. For example, if the total number of rows is 50, and there are five buckets, each bucket will contain 10 rows.

Oracle:

And the same function is available in Oracle as well. And the same code shown above will work in Oracle as well.  Just replace SYS.OBJECTS with ALL_OBJECTS.

Pretty simple way of splitting a large data set into non-overlapping sets, isn’t it!

Resources:

  • SQL Server MSDN BOL entry for the NTILE() function – here.
  • Oracle documentation of the NTILE() function – here.

Posted in Oracle, SQL Server | Leave a Comment »

Creating an adhoc ASH (Active session history) report using timeframe

Posted by decipherinfosys on July 31, 2009

In one of our previous blog post, we demonstrated that how can we generate adhoc awr report based on the given range of snap_id.  As we are aware that default timeframe for collecting snapshot data is every hour and data is retained up to 7 days. Now let us assume that snapshot is taken every hour but we want some statistics within one hour period let’s say between 1:15 PM and 1:30 PM or between 2:17 PM to 2:48 PM etc. How can we generate report for such time frame? Again we can resort back to DBMS_WORKLOAD_REPOSITORY package. This package has quite a few procedures, which can help us out to troubleshoot the problem. Keep in mind that Oracle always recommends using awrrpt.sql and ashrpt.sql to generate the AWR or ASH reports. In this blog post, we will see once again how we can get the data for specific time range using ASH_REPORT_TEXT procedure.

ASH data is sampled every second and whenever AWR snapshot is flushed to the disk, content from v$active_session_history is also flushed to the disk. It system during that time is heavily utilized, then only sample data will be flushed to the disk.  Historical data is stored in another view called DBA_HIST_ACTIVE_SESS_HISTORY. Now let us get the data:

select dbid,instance_number ino,begin_interval_time,end_interval_time
from dba_hist_snapshot
order by begin_interval_time desc

DBID        INO    BEGIN_INTERVAL_TIME        END_INTERVAL_TIME

2234839021    1    7/29/2009 5:00:46.793 PM    7/29/2009 5:30:37.953 PM
2234839021    1    7/29/2009 4:30:55.638 PM    7/29/2009 5:00:46.793 PM
2234839021    1    7/29/2009 4:00:01.491 PM    7/29/2009 4:30:55.638 PM
2234839021    1    7/29/2009 3:30:10.348 PM    7/29/2009 4:00:01.491 PM

Now, we know the value of dbid, instance_number and interval start time and interval end time. We can take this time as is and run report for the time range or we can run report for in between time range as well. In this case, we will run report for in between time range. But before that here is the signature of the procedure.

DBMS_WORKLOAD_REPOSITORY.ASH_REPORT_TEXT(
l_dbid          IN NUMBER,
l_inst_num      IN NUMBER,
l_btime         IN DATE,
l_etime         IN DATE,
l_options       IN NUMBER    DEFAULT 0,
l_slot_width    IN NUMBER    DEFAULT 0,
l_sid           IN NUMBER    DEFAULT NULL,
l_sql_id        IN VARCHAR2  DEFAULT NULL,
l_wait_class    IN VARCHAR2  DEFAULT NULL,
l_service_hash  IN NUMBER    DEFAULT NULL,
l_module        IN VARCHAR2  DEFAULT NULL,
l_action        IN VARCHAR2  DEFAULT NULL,
l_client_id     IN VARCHAR2  DEFAULT NULL)
RETURN awrrpt_text_type_table PIPELINED;

We already know the value for l_Dbid, l_instnum, l_btime and l_etime from the first sql. For rest of the parameters we will go with the default values. Here is the sql to run for a specific time range.

SELECT output
FROM
TABLE(dbms_workload_repository.ash_report_text
(2234839021,1,
to_Date('07/29/2009 16:10','MM/DD/YYYY HH24:MI'),
to_date('07/29/2009 16:25','MM/DD/YYYY HH24:MI'))
);

We can spool the output to the file from above sql and open it up as a text file to investigate the results. Here is the sample that confirms that report is generated for a specified time frame.

OUTPUT

Analysis Begin Time:   29-Jul-09 16:10:00
Analysis End Time:   29-Jul-09 16:25:00
Elapsed Time:        15.0 (mins)
Sample Count:         527
Average Active Sessions:        0.59
Avg. Active Session per CPU:        0.05
Report Target:   None specified

As mentioned earlier, policy to collect AWR data varies from company to company as each company has its own set of standards to follow.

Resources:

  • Oracle-base article – here.
  • Oracle 10g Performance Tuning guide – here.

Posted in Oracle | 1 Comment »

Getting previous date in korn shell on AIX (ksh)

Posted by decipherinfosys on July 28, 2009

Recently at one of our client site we have to write shell script, which runs every night and goes through the database errors from logs of previous day.  Now there are different ways of manipulating date in the korn shell it self but for all of that we have to write logic. Since we have to go through files of previous day, we have to be extra careful for range conditions. Ex. For 1st March we have to process data of 28th or 29th February based on the leap year. On 1st January we have to process data for 31st December of the previous year etc.

Instead of doing date arithmetic in the Korn shell, we decided to use Oracle database’s date logic since we can manipulate system date (sysdate) easily. In the following shell script we are showing 3 methods of obtaining the previous day’s date. Please copy the following contents into one file on AIX box and save it as a test.sh file and then execute the script. Before execution please make sure that you have proper privilege to execute the script.

#!/bin/ksh

DB_USER=${1}
DB_PSWD=${2}
DB_NAME=${3}

############
#Method 1:
############
v_prev_date=`TZ=bb24 date +%Y%m%d`
echo ‘Yesterday –> ‘$v_prev_date

##############################################
# Method 2:
# one way of getting system date from Oracle
# and pass it to shell script variable.
##############################################
SQLQUERY=”select to_char(sysdate – 1,’YYYYMMDD’),to_char(sysdate,’YYYYMMDD’) from dual”
print “
set pagesize 0;
set feedback off;
set heading off;
$SQLQUERY;
“| sqlplus -S $DB_USER/$DB_PSWD@$DB_NAME > TEMP_FILE

v_prev_date=`cat TEMP_FILE | awk ‘{print $1}’`
v_curr_date=`cat TEMP_FILE | awk ‘{print $2}’`
print ‘Method 2 –> ‘$v_prev_date
print ‘Method 2 –> ‘$v_curr_date

###################################################
# Method 3:
# Alternate way of getting system date from Oracle
# and pass it to shell script variable.
###################################################
var1=`sqlplus -S $DB_USER/$DB_PSWD@$DB_NAME << EOF
set echo off term off feed off ver off pages 0
select to_char(sysdate – 1,’YYYYMMDD’), to_char(sysdate,’YYYYMMDD’) from dual;
exit;
EOF`

v_prev_date=`echo $var1 | awk ‘{print $1}’`
v_curr_date=`echo $var1 | awk ‘{print $2}’`

print ‘Method 3 –> ‘$v_prev_date
print ‘Method 3 –> ‘$v_curr_date

Upon executing above shell script, we will get following results.

./test.sh scott tiger orcl

Result is

Yesterday –> 20090726
Method 2 –> 20090726
Method 2 –> 20090727
Method 3 –> 20090726
Method 3 –> 20090727

To display first line in the output, we are using AIX’s date command along with TZ (timezone) variable. 24 is offset to get the previous date and bb is just the string. It can be any string. Instead of ‘bb’ one can use ‘aa’ or ‘deci’ etc. If instead of 24 we use 48, then it will display day before yesterday’s date.

In method 2, output of SQL is written to file (temp_file) and then we are reading the file to get the date and previous date both.

In method 3, instead of writing the output to file, we are assigning it straight to shell variable and then from that shell variable, we are using awk command to get the desired result. Since we are relying on database’s date every condition is taken care of. So be it a 1st January 2010 or 1st March of 2008, we are going to get correct result of 20091231 and 20080229 respectively.

This shell script also serves as an example of how we can assign SQL*Plus output to shell variable. In most scenarios, we pass shell variable to SQL script but in this case it is reverse.

Resources:

  • Article – here.
  • Unix.com article – here.

Posted in Oracle, Unix | 3 Comments »

Back to the basics: Explicit & Implicit cursors

Posted by decipherinfosys on July 11, 2009

Today in this blog post, we will re-visit most fundamental construct used in PL/SQL programming called “cursors”. Cursors are nothing but the private SQL area in the memory. Cursors provide mechanism to work on individual records by looping through it when more than one records are fetched from the database.

They are divided in two categories:
•    Implicit cursors
•    Explicit cursors

Implicit cursors:

Implicit cursors are also known as SQL Cursors. Oracle opens implicit cursors for every DML (Insert/Update/Delete) statement. It also opens implicit cursor for the ‘select’ statement, which returns exactly one row.  With implicit cursors we don’t have to worry about opening and closing the cursors and fetching the data from the cursors. Oracle handles it automatically so we don’t have to write code to handle it. But there are certain cursor attributes available through which we can track the information about execution of the implicit cursors. Cursor attributes are

•    %FOUND (Returns True/False)
•    %NOTFOUND (Returns True/False)
•    %ISOPEN (Always returns False in case of implicit cursors as cursor is closed internally immediately after execution)
•    %ROWCOUNT (# of records effected by DML or SELECT statement)

Let us start with an example to see how we can track cursor execution information using these attributes. First of all we will create test table to work with.

SQL> CREATE TABLE TEST AS SELECT * FROM USER_OBJECTS;

Now we will write small PL/SQL block that has select statement and one DML statement as well to check the attributes of the implicit cursor.

SET SERVEROUTPUT ON;

DECLARE
v_object_name test.object_name%type;

BEGIN

IF SQL%FOUND IS NULL AND SQL%NOTFOUND IS NULL THEN
dbms_output.put_line(‘Attribute values are NULL’);
END IF;

—–SELECT
SELECT object_name
INTO v_object_name
FROM test
WHERE object_Type = ‘TRIGGER’;

dbms_output.put_line(‘SELECT RowCount = ‘ || SQL%ROWCOUNT);

—— DELETE statement
DELETE FROM TEST WHERE OBJECT_TYPE = ‘VIEW’;
IF SQL%FOUND THEN
dbms_output.put_line(‘DELETE RowCount = ‘ || SQL%ROWCOUNT);
END IF;

—— UPDATE STATEMENT
UPDATE TEST
SET STATUS = ‘INVALID’
WHERE object_name =’ HELLO’;

IF SQL%NOTFOUND THEN
dbms_output.put_line(‘UPDATE RowCount = ‘ || SQL%ROWCOUNT);
END IF;

END;
/

Here is the output of the execution.

Attribute values are NULL
SELECT RowCount = 1
DELETE RowCount = 4
UPDATE RowCount = 0

PL/SQL procedure successfully completed.

Above example, gives the results for all the cursor attributes except %isopen. Notice the first line of output. %found and %notfound attributes has null value before the execution of the statement. If dml statement effects one or more than one rows then %found is true otherwise it is false. Similarly if dml statement doesn’t effect any rows, %notfound is true else false.

Apart from these regular attributes, implicit cursor has one more attribute SQL%BULK_ROWCOUNT when we are using bulk collect to fetch multiple records. We have covered it in our previous blog post http://decipherinfosys.wordpress.com/2007/10/12/bulk-collect-and-rowcount-sqlbulk_rowcount/

Explicit cursors:

When user has to work on specific record for processing, he/she has to define explicit cursor. Cursor is called explicit when user, names the cursor and associate it with query. This is the very first step when dealing with explicit cursors. It is known as defining or declaring a cursor. Next three phases are

•    Opening a cursor (This will initialize the cursor and identifies the result set.)
•    Fetching data from a cursor (This will retrieve the result set for the executed query)
•    Closing a cursor (Once execution is done, disable the cursor)

Explicit cursors also have the same attributes as implicit cursors but when used they are precede with the cursor name. Let us see it with an example now.

SET SERVEROUTPUT ON;
DECLARE
v_object_name test.object_name%type;
v_status test.status%type;
v_opn_stat VARCHAR2(5);

CURSOR c1 IS
SELECT object_name,status
FROM test
WHERE object_Type = ‘VIEW’;
BEGIN

OPEN c1;
IF C1%ISOPEN = TRUE THEN
v_opn_stat := ‘TRUE’;
ELSE
v_opn_stat := ‘FALSE’;
END IF;

dbms_output.put_line(‘Cursor Open Status = ‘ || v_opn_stat);

IF C1%FOUND IS NULL AND C1%NOTFOUND IS NULL THEN
dbms_output.put_line(‘Attribute value is NULL’);
END IF;

LOOP
FETCH c1 INTO v_object_name,v_status;

EXIT WHEN C1%NOTFOUND;

dbms_output.put_line(‘Fetch RowCount = ‘ || C1%ROWCOUNT);

END LOOP;

CLOSE C1;

END;
/

In above PL/SQL block, we have covered the attributes of the explicit cursor. %found and %notfound condition gives an error ‘invalid cursor’ if they are used before opening  a cursor. Since Boolean variable can’t be used in dbms_output,put_line procedure, we are assigning the value to the varchar variable based on the condition.

One thing to remember that cursor attributes always correspond to the last SELECT or last DML statement executed. If two or more DML statements are fired back to back and user is checking the cursor attribute, it will retain results only for the last statement. If we need to check the cursor attribute for the first statement or any interim statement later in the code, we need to assign it to temporary variable and later on use the temporary variable for checking.

One can also pass parameter to the cursor and use the parameter values in the query rather than passing static values.  We will cover it in more detail in our future blog post.

Resources:

  • Articles: – here and here.
  • Oracle documentation – here.

Posted in Oracle | Leave a Comment »

Inserting output records from stored procedure into a table in Oracle

Posted by decipherinfosys on July 7, 2009

In one of our how to articles, we had covered how we can return records from a stored procedure to the calling program. In this blog post, we will extend it further to see how can we insert records returned from stored procedure into a table. We are going to use the same example that we had used in our how to document but for ease of understanding and reading we are showing it here again.  Please create the following objects in your test schema.

CREATE TABLE Invoice
(
INVOICE_NUMBER      NUMBER(9) NOT NULL,
INVOICE_DATE        DATE NOT NULL,
CLIENT_ID           NUMBER(9)     NOT NULL,
INVOICE_AMT         NUMBER(9,2) DEFAULT 0 NOT NULL,
PAID_FLAG           NUMBER(1) DEFAULT 0 NOT NULL, — 0 Not paid/ 1 paid
CONSTRAINT PK_INVOICE PRIMARY KEY(INVOICE_NUMBER)
)
/

CREATE SEQUENCE INVOICE_SEQ
START WITH 1
CACHE 100
/

INSERT INTO INVOICE(Invoice_Number, Invoice_date, client_ID, Invoice_Amt)
VALUES(INVOICE_SEQ.NEXTVAL, sysdate,101,1100.00);

INSERT INTO INVOICE(Invoice_Number, Invoice_date, client_ID, Invoice_Amt)
VALUES(INVOICE_SEQ.NEXTVAL, sysdate,102,1100.00);

INSERT INTO INVOICE(Invoice_Number, Invoice_date, client_ID, Invoice_Amt)
VALUES(INVOICE_SEQ.NEXTVAL, sysdate,103,1100.00);

INSERT INTO INVOICE(Invoice_Number, Invoice_date, client_ID, Invoice_Amt)
VALUES(INVOICE_SEQ.NEXTVAL, sysdate,104,1100.00);

/* Create package */
CREATE OR REPLACE PACKAGE types
AS
type cursorType is ref cursor;
END;
/

/* Here we have declared cursor variable of type cursorType as an output variable.*/
CREATE OR REPLACE PROCEDURE DEC_RTN_RECORDSET
(
p_InvoiceDate      IN   DATE,
p_ResultSet        OUT  TYPES.cursorType
)
AS
BEGIN
OPEN p_ResultSet FOR
SELECT Invoice_Number, Invoice_Date, Client_ID, Invoice_Amt
FROM Invoice
WHERE Invoice_date <= p_InvoiceDate
ORDER BY Invoice_number;

END DEC_RTN_RECORDSET;
/

Since we also want to insert into another table, we will create new table as well to store the returned records.

CREATE TABLE Invoice_New
(
INVOICE_NUMBER      NUMBER(9) NOT NULL,
INVOICE_DATE        DATE NOT NULL,
CLIENT_ID           NUMBER(9)     NOT NULL,
INVOICE_AMT         NUMBER(9,2) DEFAULT 0 NOT NULL,
PAID_FLAG           NUMBER(1) DEFAULT 0 NOT NULL, — 0 Not paid/1 paid
CONSTRAINT PK_INVOICE_NEW PRIMARY KEY(INVOICE_NUMBER)
)
/

Now instead of returning record set from stored procedure to calling program, we will write small pl/sql block to insert the records into new table Invoice_New.

DECLARE
v_resultSet TYPES.CURSORTYPE;
v_Inv_no    Invoice.Invoice_number%type;
v_Inv_dt    Invoice.Invoice_date%type;
v_Inv_amt   Invoice.Invoice_Amt%type;
v_clnt_id   Invoice.Client_Id%type;

BEGIN
/* Call the stored procedure*/
DEC_RTN_RECORDSET(sysdate, v_resultSet);

LOOP
FETCH v_resultSet INTO v_Inv_no,v_inv_Dt,v_clnt_id,v_inv_amt;

EXIT WHEN v_Resultset%NOTFOUND;
dbms_output.put_line(‘Invoice # = ‘ || v_inv_no);

/* Now insert it into the table.*/
INSERT INTO INVOICE_NEW(Invoice_Number,Invoice_Date,Client_id,Invoice_Amt)
VALUES (v_Inv_no,v_Inv_dt,v_clnt_id,v_Inv_amt);

END LOOP;

close v_resultset;
commit;

EXCEPTION
WHEN OTHERS THEN
raise_Application_Error(-20001,’Inv_No=’||v_inv_no||’–>’||SQLERRM);
END;
/

In above PL/SQL block, we haven’t opened the cursor. After procedure call was done, we straightaway started fetching records into local variables. This is because, cursor is already opened in the procedure itself using ‘OPEN … FOR’ syntax.  Since we are returning multiple records, we are putting it into the loop to fetch records one by one and inserting into new table. Once execution is done, examine invoice_new table. You will find all the records from invoice table into this new table.

Now what we will do if we have to test such a procedure. Let us recap this with the small testing block. Connect to SQL*Plus and execute following block.

VARIABLE resultSet  REFCURSOR
EXEC DEC_RTN_RECORDSET(sysdate, :resultSet);
PRINT :resultSet

The above block will return the records and will display it in SQL*Plus session. Procedure can be called from other calling programs as well in such case, data will be returned to the calling program.

Posted in Oracle | 2 Comments »

map : Permission Denied after client upgrade to 10.2.0.4

Posted by decipherinfosys on July 1, 2009

Recently at one of our client site we ran into issue after we upgraded oracle client from 10.2.0.2 to 10.2.0.4.  After update to 10.2.0.4 when we invoked SQL*Plus connection, we ran into following error. Even after displaying the error, it still connects to SQL*Plus. This error occurs only when osuser is not part of “dba” group on unix box.

sqlplus inter/inter@orcl
1634460 : map : Permission Denied
1634460 : map : Permission Denied
1634460 : map : Permission Denied

SQL>

Clearly this is a permission issue, even after running all the required scripts after upgrade was done.  After checking with Oracle, they suggested to run changeperm.sh script from install directory  under $ORACLE_HOME (Unix environment variable where oracle software is installed). This script relaxes the permission on the directories.  Here is the script followed by its output.

sh $ORACLE_HOME/install/changePerm.sh

——————————————————————————-
Disclaimer: The purpose of this script is to relax permissions on some of the
files in the database Oracle Home so that all clients can access them.
Please note that Oracle Corporation recommends using the most restrictive file
permissions as possible for your given implementation.  Running this script
should be done only after considering all security ramifications.
——————————————————————————-

-n Do you wish to continue (y/n) [n]:
y
Spooling the error log /tmp/changePerm_err.log…
Finished running the script successfully

After running the script, we tried to connect using SQL*Plus and it worked fine.

sqlplus inter/inter@orcl

SQL*Plus: Release 10.2.0.4.0 – Production on Mon May 11 22:10:20 2009

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 – 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL>

You may or may not run into this error, but it is always good to know in case you run into such scenario, there is a solution available.

Resources:

  • Orafaq – here.
  • Database-Diva Blog – here.

Posted in Oracle | Leave a Comment »

Combining multiple trace files into single file using trcsesse

Posted by decipherinfosys on June 24, 2009

We are all aware that tkprof and trace facility are two most basic and important aspects of performance tuning in oracle database. We can generate trace for the entire database or for a specific session. We have covered about them in our previous blog post – here. We have also covered event level tracing here.

Problem occurs when tracing spans across multiple trace files. How we can collect data from multiple trace files? Oracle provides the utility called ‘trcsess’, which can combine multiple trace files into single trace file. Once all the files are combined into one file, we can run tkprof or trace analyzer on the file to collect more meaningful data. Basic syntax for trcsess utility is as follows.

Trcsess utility provides different criteria on which we can combine information from all the trace files into single trace file. Following is the basic syntax for trcsess utility.

trcsess [output=output_file_name]

[session=session_id]

[clientid=client_id]

[service=service_name]

[action=action_name]

[module=module_name]

[trace_files]

Output specifies the output file name and rest of them are criteria on which one can combine the different trace files into single trace file. We can combine multiple trace files based on either session or client id or service (instance name) or specific action or module name. All these information will be available in trace file itself. After that we have to specify all the trace files we would like to combine. So here is the example of it.

trcsess output=ora_jun23_lvl12.trc service=ORCL orcl_ora_1024490_TEST.trc orcl_ora_1024492_TEST.trc

In our example, we are combining two trace files based on the service and combined output is created in ora_jun23_lvl12.trc file. As mentioned earlier, we can run tkprof output on this single file to get all the information for the traced session.

Resources:

  • Oracle 10g Performance Tuning guide – here

Posted in Oracle | Leave a Comment »

Mutating table/trigger error and how to resolve it

Posted by decipherinfosys on June 22, 2009

Most of us who have worked in Oracle have encountered ORA-04091 (table xxx is mutating. Trigger/function might not see it) at some time or the other during the development process.  In this blog post, we will cover why this error occurs and how we can resolve it using different methodology.

Mutating error normally occurs when we are performing some DML operations and we are trying to select the affected record from the same trigger. So basically we are trying to select records in the trigger from the table that owns the trigger. This creates inconsistency and Oracle throws a mutating error. Let us take a simple scenario in which we have to know total number of invalid objects after any object status is updated to ‘INVALID’. We will see it with an example. First let us create a table and then trigger.

SQL> CREATE TABLE TEST
2  AS SELECT * FROM USER_OBJECTS;

Table created.

CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
FOR EACH ROW
DECLARE
v_Count NUMBER;
BEGIN

SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;

dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;
/

Now if we try to change the status of any object to ‘INVALID’, we will run into mutating error as we are trying to update the record and trigger is trying to select total number of records in ‘INVALID’ status from the same table.

SQL> update test
2  set status = 'INVALID'
3  where object_name = 'TEST1';
update test
*
ERROR at line 1:
ORA-04091: table SCOTT.TEST is mutating, trigger/function may not see it

Having said that there are different ways we can handle mutating table errors. Let us start taking one by one scenario.

First one is to create statement level trigger instead of row level. If we omit the ‘for each row’ clause from above trigger, it will become statement level trigger. Let us create a new statement level trigger.

CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
DECLARE
v_Count NUMBER;
BEGIN

SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;

dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;

Now let us fire the same update statement again.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'TEST1';

Total Invalid Objects are 6

1 row updated.

When we defined statement level trigger, update went through fine and it displayed the total number of invalid objects.

Why this is a problem when we are using ‘FOR EACH ROW’ clause? As per Oracle documentation, the session, which issues a triggering statement on the table, cannot query the same table so that trigger cannot see inconsistent data. This restriction applies to all the row level triggers and hence we run into mutating table error.

Second way of dealing with the mutating table issue is to declare row level trigger as an autonomous transaction so that it is not in the same scope of the session issuing DML statement. Following is the row level trigger defined as pragma autonomous transaction.

CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
v_Count NUMBER;
BEGIN

SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;

dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;

Now let is issue the update statement again and observe the results.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'TEST1';

Total Invalid Objects are 5

1 row updated.

If you closely look at the output, you will see only 5 objects shown in invalid status while statement level trigger showed 6 objects in invalid status. Let us try to update multiple objects at the same time.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name IN ('T1','T2');

Total Invalid Objects are 6
Total Invalid Objects are 6

2 rows updated.

By defining row level trigger as an autonomous transaction, we got rid of mutating table error but result is not correct. The latest updates are not getting reflected in our result set as oppose to statement level trigger. So one has to be very careful when using this approach.

In version 11g, Oracle made it much easier with introduction of compound triggers. We have covered compound triggers in a previous blog post. Let us see in this case how a compound trigger can resolve mutating table error. Let’s create a compound trigger first:

CREATE OR REPLACE TRIGGER TEST_TRIG_COMPOUND
FOR UPDATE
ON TEST
COMPOUND TRIGGER

/* Declaration Section*/
v_count NUMBER;

AFTER EACH ROW IS
BEGIN

dbms_output.put_line(‘Update is done’);

END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN

SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;

dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);

END AFTER STATEMENT;

END TEST_TRIG_COMPOUND;
/

Now let us check how many objects are invalid in the test table.

SQL> select count(*) from test where status = 'INVALID';

COUNT(*)
———-
6

Here is the update statement followed by an output.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'T2';

Update is done
Total Invalid Objects are 7

1 row updated.

Here we get correct result without getting mutating table error. This is also one very good advantage of compound triggers. There are other ways also to resolve mutating table error using temporary tables but we have discussed common ones in this blog post.

Resources:

Posted in Oracle | Leave a Comment »

Creating an Adhoc AWR (Automatic workload repository) Report

Posted by decipherinfosys on June 17, 2009

In Oracle 10g, Oracle replaced statspack with AWR to collect the performance data and reporting it in a much easier laid out fashion. AWR provides very rich functionality for performance statistics including wait events, resource intensive SQLs and other related information.  In this blog post, we will cover how we can generate the latest AWR report for troubleshooting.

By default, Oracles takes snapshot of the data every hour and keeps it for 7 days. So in order to generate desired reports, we can go back up to 7 days. Snapshot information is stored in DBA_HIST_SNAPSHOT view. Let us first get the latest snap_id to get the AWR report for latest time interval. Snap_id is unique id for each time interval specified to collect the data.

SELECT dBID,(MAX(SNAP_ID)-1) “START_SNAP”, MAX(SNAP_ID) ” END_SNAP”
FROM DBA_HIST_SNAPSHOT
GROUP BY DBID;

DBID START_SNAP   END_SNAP
———- ———- ———-
1481523096       3504       3505

Once we get the value for db_id and range of snap_ids for latest time interval, we can use it in following sql to generate the report. One has to make sure that user has an execution privilege on dbms_workload_repository package. awr_report_text procedure takes five parameters. For first four parameters we have to pass the value and for the fifth one we can keep default value. Here is the definition.

DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_TEXT(
l_dbid IN NUMBER,
l_inst_num IN NUMBER,
l_bid IN NUMBER,
l_eid IN NUMBER,
l_options IN NUMBER DEFAULT 0)
RETURN awrrpt_text_type_table PIPELINED;

Value for l_Dbid, l_bid and l_eid is obtained from the first sql. Value for instance_id we can get it using sys_context function. Usually it is 1 if it is non-rac environment. Here is the sql.

SELECT output
FROM
TABLE(dbms_workload_repository.awr_report_text
(1481523096,1,3504,3505)
);

We can spool the output to the file from above sql and open it up as a text file to investigate the results. AWR is very wide topic. This is just to show how we can get the adhoc AWR reports for a given range of snap_ids or time interval.  Oracle-base has very informative article on this and how to maintain snapshots as well (Dropping/Creating snapshots), how to modify snapshot settings to increase or decrease the time interval etc.

Most companies has proper procedures in place to generate the AWR report and store it or ftp it at common location which can be accessed by DBA and/or application development team for later diagnosis.

Resources:

  • Oracle-base article – here.
  • Oracle 10g Performance Tuning guide – here.

Posted in Oracle | 1 Comment »