Translate

Search This Blog

enable tracing in own and other's session in oracle

There are documented as well as non documented both methods available for enabling tracing in oracle sessions. Some trace files like trace for event 10046 requires tkprof utility to format them while some trace files like for 10053  does not require

Documented method:

enable/disable trace in own session,requies alter session system privilege
Alter session set sql_trace=true; --enaled basic tracing
Alter session set sql_trace=false;  --disables basic tracing


or dbms_session.swt_sql_trace(sql_trace=>true); enabled basic tracing
dbms_session.swt_sql_trace(sql_trace=>false);  --disabled basic tracing

to enable/disable tracing in other's session using DBA login
  • dbms_system.set_sql_trace_in_session(sid,serial,true) --enabled tracing
  • dbms_system.set_sql_trace_in_session(sid,serial,false) --disabled tracing
Undocuemnted methods:

  • dbms_system.set_ev(sid,serial#,10046,1,''); --enables basic tracing
  • dbms_system.set_ev(sid,serial#,10046,4''); --enables tracing with bind values capturing
  • dbms_system.set_ev(sid,serial#,10046,8,'');--enables tracing with waits events information capturing
  • dbms_system.set_ev(sid,serial#,10046,12,'');--enables tracing with bind values + wait events
  • dbms_system.set_ev(sid,serial#,10046,0,''); --disables tracing
its documented equilavent has come into oracle 10g:

DBMS_SUPPORT.start_trace(waits=>TRUE, binds=>FALSE);

DBMS_SUPPORT.stop_trace;
DBMS_SUPPORT.start_trace_in_session(sid=>123, serial=>1234, waits=>TRUE, binds=>FALSE);

EXEC DBMS_SUPPORT.stop_trace_in_session(sid=>123, serial=>1234);

 Optimizer thought proces and sort tracing:

  • dbms_system.set_ev(sid,serial#,10053,1,''); --enables formatted tracing for optimizer information
  • dbms_system.set_ev(sid,serial#,10033,1,''); --enables formatted tracing trace sorts for query

Undocumented enabling tracing by setting events methods:

  • ALTER SESSION SET events ''10046 trace name context forever, level 12''; --enables level 12 tracing i.e it captures bind data and wait events also
  • ALTER SESSION SET events ''10046 trace name context off''; --disables tracing
If changing the application code for enabling tracing is not possible as most are the cases then tracing can be enabled from logon triggers

CREATE OR REPLACE TRIGGER db_logon_trigger
AFTER LOGON ON DATABASE
DECLARE
BEGIN
IF USER = 'username_to_be_traced'
THEN
 EXECUTE IMMEDIATE 'ALTER SESSION SET events ''10046 trace name context forever, level 12''';
END IF;
END;
 

query to find cardinality of columns to consider for indexing

You ran sqal tuning advisory on sql tuning sets conatining vast queries and got advisory. Now you have to decide whether to create indexes based on sql tuning advisory or not. One autoamted way again is to run sql access advisor and decide. Alternate is decide after looking at cardinality of potentials columns for indexing.  Following query helps in later part.This query lists the size of table, total number of rows in table, number of distinct rows on column and total number of rows. Further it lists if the given column is foreign key  which is often considered as candidate for indexing ,potentially good for join query with parent table. This query also lists the columns names present in indexes along with their order in case of composite index. Further this list primary keys and unique keys also. Prerequisite of accuracy of this query is table should be analyzed.

SELECT  t2.table_name,  t3.last_analyzed,  t3.tsize "Table Size(MB)",  t2.column_name,  t2.column_position "Col position in Indx",  t2.index_name,  t2.isize "Index Size(MB)" ,  DECODE(t1.constraint_type,  'P',  'Pkey',  'U',  'U key',  'R',  'Fkey') TYPE ,  DECODE(t2.uniqueness,  'UNIQUE',  'Yes',  'No') "UNIQUE",  t2.index_type ,  t3.num_distinct,  t3.num_rows
 FROM
 ( SELECT uc.TABLE_NAME,  ucc.COLUMN_NAME,  ucc.POSITION,  uc.constraint_type FROM user_constraints uc ,  user_cons_columns ucc  WHERE  uc.TABLE_NAME=ucc.TABLE_NAME AND  uc.CONSTRAINT_NAME=ucc.CONSTRAINT_NAME AND  constraint_type IN ('P','U','R' ) )
t1 ,
 ( SELECT ui.table_name,ui.index_name,ROUND(ui.leaf_blocks*16384/1024/1024/1024,3) isize,  ui.uniqueness,uic.COLUMN_NAME,uic.column_position, ui.index_type  FROM user_indexes ui ,user_ind_columns uic
WHERE ui.INDEX_NAME=uic.INDEX_NAME ) t2,
( SELECT uc.last_analyzed,uc.table_name,ROUND(uc.blocks*16384/1024/1024/1024,3)   tsize,utc.column_name,uc.num_rows,utc.num_distinct  FROM user_tables uc, user_tab_columns utc WHERE uc.table_name=utc.table_name ) t3
WHERE t1.table_name(+)=t2.table_name
AND  t1.column_name(+)=t2.column_name
AND  t3.table_name(+)=t2.table_name
AND  t3.column_name(+)=t2.column_name
ORDER BY 1,6,5 desc

oracle stored outline and sql plan baseline, 11gR2,sql plan baseline overrides the stored outline

SQL PLAN BASELINE is wonderful feature of Oracle 11g. SQL plan baselines can be created in many ways; created from sql tuning sets,from the cursor cache, from migrating stored outline.This post shows- how to create stored outline, migrate outline to sql plan baseline, check use of sql plan baseline,check sql plan base line overrides stored outline

One additional point :Many people claims that before version oracle 11g sql profile is good option to gain plan stability but this is not true as sql profiles contains only statistical information like opt_estimate information to drive optimizer towards good plan but over time if statistical composition changes profile may no longer be helping query and execution plan may be sub optimal. In this case stored outlines are viable option from oracle 8i to gain plan stability before oracle 11g. From the evolution of sql plan baseline they are the best options. Further one great compatibility of sql plan baseline with stored outline is they can be created from stored outline using migrate_stored_outline procedure of package dbms_spm.

SQL> conn / as sysdba
Connected.

SQL> grant CREATE ANY OUTLINE to scott;
Grant succeeded.

SQL> conn scott/tiger
Connected.

SQL> create outline ol1 for category categ1 on select * from emp where deptno=10

Outline created.

SQL> select * from user_outlines;

NAME CATEGORY USED TIMESTAMP SIGNATURE COMPATIBLE ENABLED FORMAT

------------------------------ ------------------------------ ------ ----------- ----------------------------------------------------------------- --------------

MIGRATED

------------

OL1 CATEG1 UNUSED 19-JUL-13 mp where deptno=10 0B6183E104E1FA

NOT-MIGRATED

SQL> select name,category,sql_text from user_outlines;

NAME CATEGORY SQL_TEXT

------------------------------ ------------------------------ ------------------

OL1 CATEG1 select * from emp

SQL> var mig clob

SQL> exec :mig:=dbms_spm.migrate_stored_outline(attribute_name=>-

> 'outline_name',attribute_value=>'OL1',fixed=>'NO')

BEGIN :mig:=dbms_spm.migrate_stored_outline(attribute_name=> 'outline_name',attribute_value=>'OL 1',fixed=>'NO'); END;
*
ERROR at line 1:

ORA-38171: Insufficient privileges for SQL management object operation

ORA-06512: at "SYS.DBMS_SPM", line 3416

ORA-06512: at line 1

SQL> conn / as sysdba
Connected.

SQL> grant execute on dbms_spm to scott;

Grant succeeded.

SQL> conn scott/tiger
Connected.

SQL> exec :mig:=dbms_spm.migrate_stored_outline(attribute_name=>-

> 'outline_name',attribute_value=>'OL1',fixed=>'NO')

BEGIN :mig:=dbms_spm.migrate_stored_outline(attribute_name=> 'outline_name',attribute_value=>'OL 1',fixed=>'NO'); END;
*
ERROR at line 1:
ORA-38171: Insufficient privileges for SQL management object operation
ORA-06512: at "SYS.DBMS_SPM", line 3416
ORA-06512: at line 1

SQL> REM it shows granting execute privilege on dbms_spm is not enough

SQL> conn / as sysdba
Connected.

SQL> grant ADMINISTER SQL MANAGEMENT OBJECT to scott;

Grant succeeded.

SQL> conn scott/tiger
Connected.

SQL> exec :mig:=dbms_spm.migrate_stored_outline(attribute_name=>-
> 'outline_name',attribute_value=>'OL1',fixed=>'NO')

BEGIN :mig:=dbms_spm.migrate_stored_outline(attribute_name=> 'outline_name',attribute_value=>'OL 1',fixed=>'NO'); END;
*
ERROR at line 1:
ORA-18007: ALTER ANY OUTLINE privilege is required for this operation
ORA-06512: at "SYS.DBMS_SPM", line 3416
ORA-06512: at line 1

SQL> conn / as sysdba
Connected.

SQL> grant ALTER ANY OUTLINE to scott;
Grant succeeded.
SQL> conn scott/tiger
Connected.

SQL> exec :mig:=dbms_spm.migrate_stored_outline(attribute_name=>-

> 'outline_name',attribute_value=>'OL1',fixed=>'NO')

PL/SQL procedure successfully completed.
SQL> select name,sql_text,migrated from user_outlines;

NAME SQL_TEXT M IGRATED

------------------------------ ----------------------------------------------------------------- --------------- ------------

OL1 select * from emp where deptno=10 M IGRATED

SQL> conn / as sysdba
Connected.

SQL> select sql_handle,sql_text,plan_name,origin,enabled,accepted from dba_sql_plan_baselines;

SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC

------------------------------ -------------------------------------------------------------------------------- ------------------------------ -------------- --- ---

SYS_SQL_5b35ebd3a9ade57e select * from emp where deptno=10 OL1 STORED-OUTLINE YES YES

SQL> conn scott/tiger
Connected.

SQL> set autot on

SQL> select * from emp where deptno=10;

Execution Plan

----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
Note
-----
- SQL plan baseline "OL1" used for this statement


SQL> Now we check sql plan base line overrides stored outline

SQL> alter session set use_stored_outlines=categ1;
Session altered.

SQL> select * from emp where deptno=10;
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
Note
-----
- SQL plan baseline "OL1" used for this statement

Here we see inspite of enabling of use of stored outline of category categ1 execution plan is showing sql plan base line has been used. So we can say 11gR2 sql plan base overrides the stored outline

Stored Outline Quick Reference [ID 67536.1]

How to Use SQL Plan Management (SPM) - Example Usage [ID 456518.1]



compressed archivelog backup set in 11g

Oracle 10g brought compressed backupset feature which is wonderful especially when backing up large data warehouse databases. This not only saves the disk space but also the backup time is reduced. This comes at only little price of CPU. But archivelogs could not be compressed in 10g. From 11g onwards archive logs also can be compressed as backup set.

RMAN> backup as compressed backupset archive log from sequence=2000

RMAN> backup as compressed backupset archive log from sequence=2000 until time sysdate-10*1/1440

If any of archive logs is physicall deleted then archive log backup can fail. In this case
RMAN> change archivelog all validate or RMAN>crosscheck archivelog all comes handy.

If you are in oracle 10g you can backup archive logs using below shell script

#!/bin/bash
# change directory to archive log location
cd /apps/oracle/archive_logs
# it is assumed log_archive_format has .arc in last
ls -t *.arc >file_list
# sed is used to leave latest two archive logs from compression as they might be stilll being generated
cat file_list|sed '1,2d'>file_list_to_compress
for file in `cat file_list_to_compress`
do
gzip $file
done

block corruption using RMAN in oracle,also simulate block corruption

Check and repair physical and logical block corruption :

Block corruption errors are not detected in oralce until you run dbv,RMAN,analyze or export command etc. As a DBA you can proactively monitor the block corruptions in Oracle using RMAN. Further you can recover these block corruptions using RMAN with little efforts using healthy RMAN or user managed backup. If you are using user managed backup then this will need to be cataloged in oracle using catalog datafilecopy 'datafile_path_name'

When you take RMAN backup using backupset or backup image copy or run RMAN validate command it automatically checks for physical block corruptions(media blcok corruptions, hard corruption) and RMAN aborts at the first block corruption it encounters. You may want RMAN to check completely, in this case you need to allow RMAN explicitly for N number of block corruptions it encounters.

RUN { set maxcorrupt for datafile 1,2,3 ,4 to 200;
                  backup validate database;}

Since oracle 9i and onward we always have a default channel so there is no need to allocate one.

check physical as well as logical block corruption(software corruption):

RUN { set maxcorrupt for datafile 1,2,3 ,4 to 200;
          backup validate check logical database;}

Above commands mark the blocks as corrupt similar to dbv and populated v$database_block_corruption.
V$database_block_corruption gives you the file number of the datafiles that contain the corruption, and will provide the block number at which it starts and the number of blocks corrupted. You can find the object(s) affected by this corruption in the dba_segments view. using the provided values for file_id & block_id. Don't forget to check the indexes or constraints for any affected objects.

SQL> select * from v$database_block_corruption;

FILE# BLOCK# BLOCKS CORRUPTION_CHANGE# CORRUPTIO
---------- ---------- ---------- ------------------ ---------
4 756652 1 5.5157E+12 CORRUPT

select segment_name, segment_type, owner

from dba_extents
where file_id = 4
and 756652 between block_id
and block_id + blocks -1;

no rows selected

Question is is corrupted block the free block ?

 [ create table block_corr as select file#,block# from V$DATABASE_BLOCK_CORRUPTION where 1=0;

create unique index bcorr_pk on block_corr(file#,block#);
insert into block_corr select file#,block# from V$DATABASE_BLOCK_CORRUPTION order by file#,block#;

create table corr_objects as
SELECT de.segment_type, de.owner, de.segment_name, de.PARTITION_NAME
FROM dba_extents de, block_corr bc
WHERE de.file_id = bc.file#
and bc.block# between block_id AND block_id + blocks - 1
group by de.segment_type, de.owner, de.segment_name, de.PARTITION_NAME;
 ]

Next run this statement to identify the objects associated with corruption:

SQL> SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
, greatest(e.block_id, c.block#) corr_start_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
- greatest(e.block_id, c.block#) + 1 blocks_corrupted
, null description
FROM dba_extents e, v$database_block_corruption c
WHERE e.file_id = c.file#
AND e.block_id = c.block#;

OWNER SEGMENT_TYPE SEGMENT_NAME PARTITION_NAME FILE#
---------- -------------------- -------------------- --------------- ----------
CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED D
----------------- --------------- ---------------- -

TEST INDEX XXXXXXXXXXXX 62
ME_IDX
292779 292779 1

If the block does not belong to any object there is nothing to do, nothing to care, the block will be reformatted when it will be reallocated.

SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
, greatest(e.block_id, c.block#) corr_start_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
- greatest(e.block_id, c.block#) + 1 blocks_corrupted
, null description
FROM dba_extents e, v$database_block_corruption c
WHERE e.file_id = c.file#
AND e.block_id <= c.block# + c.blocks - 1
AND e.block_id + e.blocks - 1 >= c.block#
UNION
SELECT s.owner, s.segment_type, s.segment_name, s.partition_name, c.file#
, header_block corr_start_block#
, header_block corr_end_block#
, 1 blocks_corrupted
, 'Segment Header' description
FROM dba_segments s, v$database_block_corruption c
WHERE s.header_file = c.file#
AND s.header_block between c.block# and c.block# + c.blocks - 1
UNION
SELECT null owner, null segment_type, null segment_name, null partition_name, c.file#
, greatest(f.block_id, c.block#) corr_start_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
- greatest(f.block_id, c.block#) + 1 blocks_corrupted
, 'Free Block' description
FROM dba_free_space f, v$database_block_corruption c
WHERE f.file_id = c.file#
AND f.block_id <= c.block# + c.blocks - 1
AND f.block_id + f.blocks - 1 >= c.block#
order by file#, corr_start_block#;

OWNER SEGMENT_TYPE SEGMENT_NAME
------------------------------ ------------------ ---------------------------------------------------------------------------------
 PARTITION_NAME FILE# CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED DESCRIPTION
------------------------------ ---------- ----------------- --------------- ---------------- --------------

4 756652 756652 1 Free Block

Note: DBMS_REPAIR does not recover the blocks , rather it marks the corrupted blocks as fixed so these blocks are skipped in read/write operation. Data in these blocks are lost forever.

Other examples:

# Check for physical and logical corruption of a tablespace.
RMAN { VALIDATE CHECK LOGICAL TABLESPACE USERS;}

# Check for physical corruption of all archived redo logs files.
VALIDATE ARCHIVELOG ALL;

# Check for physical and logical corruption of the controlfile.
VALIDATE CHECK LOGICAL CURRENT CONTROLFILE;

# Check for physical and logical corruption of a specific backupset.
VALIDATE CHECK LOGICAL BACKUPSET 3;

# Check for physical corruption of files to be backed up.
BACKUP VALIDATE DATABASE ARCHIVELOG ALL;

# Check for physical and logical corruption of files to be backed up.
BACKUP VALIDATE CHECK LOGICAL DATABASE ARCHIVELOG ALL;

# Check for physical corruption of files to be restored.
RESTORE VALIDATE DATABASE;

# Check for physical and logical corruption of files to be restored.
RESTORE VALIDATE CHECK LOGICAL DATABASE;

Repair block corruption: RMAN can be used to recover corrupted blocks online.Without block media recovery, if even a single block is corrupt, then you must take the datafile offline and restore a backup of the datafile. You must apply all redo generated for the datafile after the backup was created. The entire file is unavailable until media recovery completes. With block media recovery, only the blocks actually being recovered are unavailable during the recovery.

Repairing All Block Corruption in the Database: Follwing runs a backup validation to populate V$DATABASE_BLOCK_CORRUPTION, then repairs any corrupt blocks recorded in the view:

RUN {
BACKUP VALIDATE CHECK LOGICAL DATABASE;
BLOCKRECOVER CORRUPTION LIST;}

OR

RECOVER CORRUPTION LIST;

Note: RMAN block media recovery required enterprise edition. 

Alternate 1 : following repairs all physically corrupted blocks recorded in the view:
 BLOCKRECOVER CORRUPTION LIST RESTORE UNTIL TIME 'SYSDATE - 7';

Other examples #1 Recovering a Group of Corrupt Blocks: This recovers corrupt blocks in three datafiles:

BLOCKRECOVER DATAFILE 2 BLOCK 12, 13 DATAFILE 3 BLOCK 5, 98, 99 DATAFILE 4 BLOCK 19;

#2 Limiting Block Media Recovery by Type of Restore:
following  recovers a series of blocks and restores only from datafile copies:

RUN {
BLOCKRECOVER DATAFILE 3 BLOCK 2,3,4,5 TABLESPACE sales DBA 4194405, 4194409, FROM DATAFILECOPY; }

#3 Limiting Block Media Recovery by Backup Tag:
This recovers blocks and restores only from the backup with the tag weekly_backup:

BLOCKRECOVER TABLESPACE SYSTEM DBA 4194404, 4194405 FROM TAG "weekly_backup";

#4 Limiting Block Media Recovery by Time: Following  recovers two blocks in the SYSTEM tablespace. It restores only from backups that could be used to recover the database to a point two days ago:

BLOCKRECOVER TABLESPACE SYSTEM DBA 4194404, 4194405 RESTORE UNTIL TIME 'SYSDATE-2';

References: 1
                  2
                  3
                  4
 finding corrupted blocks in detail

It was all about physical and logical block corruption but But what about when the data itself is "corrupted" but the block is fine? I'm not talking about a logical or application oriented corruption rather a corruption that happens when there is a "Lost Write." In versions 11.1 (Oracle says it is now corrected for 11.2 onwards) and earlier it was possible for dbwr process to get a write acknowledgement of the write when in fact it did not happen. Of course this isn't Oracle's fault, but try explaining that to your manager/director weeks or months later when a data corruption is encountered and you don't know where it came from! Check it out in the "Backup and Recovery User's Guide" as well as the parameter DB_LOST_WRITE_PROTECT.  The best protection is using Data Guard.

reference: db lost write
asl tom about db lost write & protection

Simulate block corruption in oracle:
select segment_name , header_file , header_block
from dba_segments
where segment_name = 'table_to_be_corrupted' and owner = 'user_name';

SEGMENT_NAME HEADER_FILE HEADER_BLOCK
---------------------------- ----------- ------------
table1                              8                          19

Lets corrupt block 20 using the dd command in Linux. For windows use ultra edit editor in hexa mode

DISCLAIMER: The dd command given below is just for learning purposes and should only be used on testing systems. I will not take any responsibility of any consequences or loss of data caused by this command.

$dd of=ts_corrupt01.dbf bs=8192 conv=notrunc seek=20 << EOF
Make it Corrupt.
EOF

11g Active Standby Database Automatic Block Corruption Repair(reference: Gavin Soorma)


In addition to the real time query capability of the 11g Active Data Guard feature, we can also add to our high availability capability by using the Automatic Block Media Repair feature whereby data block corruptions on the Primary database can be repaired by obtaining those blocks from the standby site – all performed by a background process (ABMR) transparent to the application. The same functionality can be used to repair block corruptions on the Active Standby site by applying blocks which are conversely now received from the Primary site.



creating stored outline from cached execution plan

Beginning from oracle 10gR2, The DBMS_OUTLN package contains a procedure called CREATE_OUTLINE that can be used to create a Stored Outline containing the execution plan of a Cursor in the Shared Pool using its Hash Value and Child Number.

PROCEDURE create_outline(hash_value IN NUMBER,
child_number IN NUMBER,
category IN VARCHAR2 DEFAULT 'DEFAULT');

In order to use the procedure, a user needs to be able to uniquely identify the Cursor for which
the outline is to be stored. In the following example, the text string: "OL_TEST" is used to do this
in order to store an outline of an illustrative query SELECT FROM V$SQL.

Example:

SQL> GRANT CREATE ANY OUTLINE TO SCOTT;

Grant succeeded.
SQL> GRANT EXECUTE_CATALOG_ROLE TO SCOTT;
select hash_value, child_number, sql_text
from v$sql
where sql_text like '%OL_TEST%';
HASH_VALUE CHILD_NUMBER SQL_TEXT
--------- ---------- -------
1556463076 0 select hash_value, child_number, sql_text from v$sql where sql_text like '%OL_TEST%'

SQL> alter session set create_stored_outlines = true; -- This step is to avoid Bug:5454975 fixed 10.2.0.4
Session altered.

SQL> exec dbms_outln.create_outline(1556463076,0);
SQL> select name from dba_outlines;
NAME
------------------------------
SYS_OUTLINE_07072313512890701

MORE on such outlines: example-

SQL> exec dbms_outln.create_outline(1556463076,0,'MYCAT');


PL/SQL procedure successfully completed.We can find the name of the newly created Stored Outline by querying e.g. DBA_OUTLINES. This will be a system-generated name at first, we can change it to a more human-friendly format using:

SQL> alter outline SYS_OUTLINE_07072313512890701 rename to MYOUTLN;

Note: prior to Oracle10g you will have to use one of the alternative methods i.e. manually create a Stored Outline using the CREATE OUTLINE command or capture it using ALTER SESSION SET CREATE_STORED_OUTLINES = .

If the Stored Outline already exists, we can change its category name to MYCAT as follows:

SQL> alter outline MYOUTLN change category to MYCAT;

When to rebuild Indexes in Oracle

Generally indexes in oracle are rebuilt more often than they should really indeed be. Richard Foote the famous expert in Oracle Indexes are not in favor of index rebuilds. Oracle Indexes are actually B+ Tree implementation so they should almost be balanced perfectly even with large number of deletes. And those deleted blocks or blocks not fully deleted but with some deleted entries can reuse the space for the new index keys so not rebuilding indexes saves expensive rebuild operation which require outage in oracle standard edition. If index has been rebuilt and space is more tight in blocks then later DMLs can be slow as they may required index block split. So traditional approach to query index_stats and deciision to rebuild index based on deleted leaf rows/blocks is no longer prudent in most of scenarios.

 Following are  Metalink notes id about Index rebuilding, these have similar thought as Richard.

122008.1

Index Rebuild, the Need vs the Implications [ID 989093.1]

Script to investigate a b-tree index structure [ID 989186.1]
Validates and rebuilds indexes occupying more space than needed  [182699.1]

click here interesting discussion in Oracle forum for index rebuild

trick with oracle stored outlines in getting good execution plan

Outlines are deprecated in 11g and may be no longer in existence in future but sometimes when there is no freedom to change the application code and query requiring tuning is performing well on other system then creating outline on good database and copying this outline data to database where query is requiring tuning can be great option.

Below  are steps: 
#1: CREATE OR REPLACE OUTLINE OUTLINE_NAME
FOR CATEGORY  CATEGORY_NAME
ON QUERY;

#2.  Next, export the outline from good database
exp userid=outln/ query="where category = ''" tables=(ol$,ol$hints)

#3 Then, import into the “bad” database, e.g.
imp userid=outln/ full=y ignore=yes

#4. If changing application code to enable use of outline is not possible which is most likely the case then use a trigger to enable the use of  outline for dbuser on bad database:

CREATE OR REPLACE TRIGGER xxxxxxxxx
AFTER LOGON ON DATABASE
DECLARE
BEGIN
IF USER = yyyyyyyyyyyyy
THEN
--
EXECUTE IMMEDIATE 'ALTER SESSION SET USE_STORED_OUTLINES = ;
--
END IF;
EXCEPTION WHEN OTHERS THEN NULL;
END;

Better alternate of exporting outline data:
Here we use the EXP utility with the QUERY parameter in order to selectively export the outline data for the Stored Outlines in category MYCAT.This allows us to transfer more than one Stored Outline in one go.

exp system/ file=myoutln.dmp tables=(outln.ol$,outln.ol$hints,outln.ol$nodes) query=\"where category='MYCAT'\"

The outline data exists in the OUTLN schema in three tables OL$, OL$HINTS, OL$NODES all of which need to be exported together.

On Unix we have to escape the special characters using a backslash, the command here would be:

exp system/ file=myoutln.dmp tables=\(outln.ol\$,outln.ol\$hints,outln.ol\$nodes\) query=\"where category=\'MYCAT\'\"





oracle enterprise manager ssl mode - getting rid of https

it is very easy, this comes handy when browsers does not support oem over https or when want fast response

emctl unsecure dbconsole

This will unsecure oracle enterprize manager and you get rid of https

export ORACLE_HOSTNAME=192.168.100.4
emctl unsecure dbconsole

Oracle Enterprise Manager 11g Database Control Release 11.2.0.1.0
Copyright (c) 1996, 2009 Oracle Corporation.  All rights reserved.
https://192.168.100.4:1158/em/console/aboutApplication
Configuring DBConsole for HTTP...   Done.
DBCONSOLE successfully stopped...   Done.
Agent is already stopped...   Done.
Unsecuring dbconsole...   Started.
DBConsole is now unsecured...  Done.
Unsecuring dbconsole...  Sucessful.
DBCONSOLE successfully restarted...   Done.

Short Circuit=>Power Down=>SAN Down=>DB crashed=>DBstarted fine=>but BLAB BLAB BLAB ...SAP Application not working properly

Short Circuit=>Power Down=>SAN Down=>DB crashed=>DB Started fine=>but BLAB BLAB BLAB ...SAP Application not working properly

User complained some modules had issues

Database Alert Log recorded ORA-600 internal error with different first actual code argument. search into Meta link told DB had problem in executing DMLs on some tables. As some of indices on these tables had logical corruption. So its obvious that when DB crashed DMLS on these tables were in progress,causing update of index entries. so BAD row mismatch occured as key-rowid information was not consistent.

automatic Crash Recovery of Database[without intervention of DBA] was successful, there was no corruption in logs nor in any datafile block. So when Db started after power SAN was made up again after restoring power supply it was opened successfully. But it had logical corruption hidden inside bad leaf blocks of indices. and crash recovery was not able to fix index logical corruption. And its obvious behavivour. We can't expect so hight from Database technology.

solution :

1. identify such indices which are in bad shape and
a) drop and recreated OR b) rebuild them online

2. restore from backup and recover


later was not feasible as DB had size 1TB and prod database was on RAID 5. It would take 3 days to restore!!!

also server did not have enough space to store all archive logs at same time.

ignoring index in aggregate query

  1* create table t7(c1 date,c2 number,c3 varchar2(100))
SQL> /
Table created.
SQL> insert into t7 select sysdate,mod(level,5),mod(level,10000) from dual connect by level<=100000;

100000 rows created.
SQL> insert into t7 select sysdate-1,mod(level,5),mod(level,10000) from dual connect by level<=10000
;
10000 rows created. 

create index i_idx on t7(c1);
index created

select TRUNC(c1, 'HH') c1 from t7
  where c1  between sysdate-5 and sysdate-1
  group by TRUNC(c1, 'HH')
   
SQL> /
C1
---------
08-JUL-13
09-JUL-13

  1  create view v1 as
  2  select TRUNC(c1, 'HH') vc1 ,count(*) vc2 from t7
  3  --where c1  between sysdate-2 and sysdate-1
  4* group by TRUNC(c1, 'HH')
SQL> /
View created.

SQL> conn scott/tiger@orcl11g
Connected.
SQL> set autot on
SQL> select * from v1 where vc1 between sysdate-2 and sysdate-1;
VC1              VC2
--------- ----------
09-JUL-13      11000

Execution Plan
----------------------------------------------------------
Plan hash value: 1490029489
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      | 12762 |   112K|   289   (6)| 00:00:04 |
|   1 |  HASH GROUP BY      |      | 12762 |   112K|   289   (6)| 00:00:04 |
|*  2 |   FILTER            |      |       |       |            |          |
|*  3 |    TABLE ACCESS FULL| T7   | 12762 |   112K|   288   (6)| 00:00:04 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(SYSDATE@!-2<=SYSDATE@!-1)
   3 - filter(TRUNC(INTERNAL_FUNCTION("C1"),'fmhh')>=SYSDATE@!-2 AND
              TRUNC(INTERNAL_FUNCTION("C1"),'fmhh')<=SYSDATE@!-1)
Note
-----
   - dynamic sampling used for this statement (level=2)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        931  consistent gets
          0  physical reads
          0  redo size
        388  bytes sent via SQL*Net to client
        387  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
SQL> exec dbms_stats.gather_table_stats(user,'t7',estimate_percent=>null,method_opt=>'for all column
s size 254',cascade=>true);
PL/SQL procedure successfully completed.


  1   select TRUNC(c1, 'HH') vc1 ,count(*) vc2 from t7
  2   where c1  between sysdate-2 and sysdate-1
  3*  group by TRUNC(c1, 'HH')
SQL> /
VC1              VC2
--------- ----------
09-JUL-13      11000

Execution Plan
----------------------------------------------------------
Plan hash value: 3216186273
----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |     8 |    45   (3)| 00:00:01 |
|   1 |  HASH GROUP BY     |       |     1 |     8 |    45   (3)| 00:00:01 |
|*  2 |   FILTER           |       |       |       |            |          |
|*  3 |    INDEX RANGE SCAN| I_IDX | 11000 | 88000 |    44   (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(SYSDATE@!-2<=SYSDATE@!-1)
   3 - access("C1">=SYSDATE@!-2 AND "C1"<=SYSDATE@!-1)

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         51  consistent gets
          0  physical reads
          0  redo size
        388  bytes sent via SQL*Net to client
        387  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

REMEDY:

SQL> create index i_idx2 on t7(TRUNC(c1, 'HH'));
Index created.
Elapsed: 00:00:08.69
SQL> select * from v1 where vc1 between sysdate-2 and sysdate-1;
VC1              VC2
--------- ----------
09-JUL-13      11000
Elapsed: 00:00:00.19
SQL> set autot on
SQL> set lines 300 pages 80
SQL> /
VC1              VC2
--------- ----------
09-JUL-13      11000
Elapsed: 00:00:00.08
Execution Plan
----------------------------------------------------------
Plan hash value: 3507030160
----------------------------------------------------------------------------------------
| Id  | Operation                     | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |        |     5 |    40 |    12   (9)| 00:00:01 |
|   1 |  HASH GROUP BY                |        |     5 |    40 |    12   (9)| 00:00:01 |
|*  2 |   FILTER                      |        |       |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID| T7     |   778 |  6224 |    11   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | I_IDX2 |  1400 |       |     6   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(SYSDATE@!-2<=SYSDATE@!-1)
   4 - access(TRUNC(INTERNAL_FUNCTION("C1"),'fmhh')>=SYSDATE@!-2 AND
              TRUNC(INTERNAL_FUNCTION("C1"),'fmhh')<=SYSDATE@!-1)

Statistics
----------------------------------------------------------
          8  recursive calls
          0  db block gets
         66  consistent gets
          0  physical reads
          0  redo size
        388  bytes sent via SQL*Net to client
        387  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Column Order in Oracle Composite Index and indexing strategy

You have queries like:

select *
from table1
where c1=10 and c2=30 and c3=100

select *
from table1
where c1=10 and c2=30

If the columns used in filter conditions are not selective i.e c1, c2 and c3 have high cardinality then these columns are not good candidate for indexing. In this case their combination can be tried to see  if whole filter condition is more selective or say cardinality of whole filter,the combination of these individual filters is low.  So in this case composite index on combinations can be created. Combination of these columns can be unique or not unique

Now question #1 is what should be the combination of these columns in composite index. In oracle we have index skip scan so answer may be slighly different from other rdbms which uses composite indexes only when leading column is used.

Suppose that we have a composite unique index on this table on the combination of these columns (in that order):

FirstName, LastName, ContactID
Let’s look at the data distribution:
select count(*) as total_records,
count(distinct FirstName) as Distinct_FirstName,
count(distinct LastName) as Distinct_LastName,
count(distinct ContactID) as Distinct_ContactID
from Employees

total_records Distinct_FirstName Distinct_LastName Distinct_ContactID
------------- ------------------ ----------------- ------------------
2336724       1018               1206              2336724


you can see, the most selective column is CONTACTID for which a given value will qualify an exact 1 record. Next most selective is LastName
and the least selective is FirstName.

Now, let’s look at Question #1 :

Query :select * from Employees where ContactID = 2 and FirstName = ‘Catherine’ and see its cost

Now, let us also include the leading column of the index:

Query : select * from Employees where ContactID = 2 and FirstName = ‘Catherine’ and see its cost

Its cost will come down. This tells us that not only is the column order important, it is very important for the leading column of the index to be part of the filter condition in order to make good use of the index and to have a good execution plan. This immediately brings another question to mind – now, we know that we need the leading column of the index to be present in the filter condition (WHERE clause) but does it make a difference if we have the least selective vs the most selective column as the leading column of that index?

In our example, we have established that the ContactID column is the most selective – every value qualifies for a single record and the FirstName column is the least selective. The index that we currently have on the table is: test and it is formed on the combination of the columns in this order:

FirstName, LastName and then ContactID

Now, let’s go ahead and create another index and this time, we will make ContactID as the first column:

create index test_2 on Employees(ContactID, LastName, FirstName)

Now, let us execute the same query but force it to use different indexes and see whether the execution plan and the query cost changes at all. The query cost will be  different in the two cases – the test index usage yielded higher cost vs lower cost in case of test_2. So the column order as well as the selectivity of the columns in an index does matter a lot . If it is SQL Server which does not have index skip scan then having the most selective column as the leading column of the index is a good design assuming that that column is going to be used in your filter criteria.

Bottom line is that the order of the columns in a composite index is very important and it also depends upon how your queries are written. Let’s take one more example:

Say, I have a table TAB1 which has columns A, B, C and suppose I have two index:
Index1 on (a, b) and Index2 on (b,a).
If I have these queries:

Select * from TAB1 where A = @a and B = @b;
Select * from TAB1 where A = @a
then, Index1 is useful for these queries. If I have:

Select * from TAB1 where A = @a and B = @b;
Select * from TAB1 where B = @b
then, Index2 is more useful since it covers both the queries.  Beginning Oracle 9i,if cost based optimizer is choosen for execution theen there is  a new feature called “Index Skip Scan” was introduced using which both Index1 or Index2 can be useful in the above case but it depends upon the selectivity of those columns – we will cover index skip scan in a future post but you can read more over here  if you are interested.

Key things to take away from this post are:
1) Order of the columns in an index is important but the most important thing is to first understand your queries and see how the index is going to be used. Say, I have three columns in a table with million records with this data distribution for the columns:
C: 2 distinct values
B: 5 distinct values
A: Million distinct values
As you can see from above, A is the most selective and C is the least so is A, B, C the right order? Answer is it depends. If you are using only equality operators in your filter condition, then definitely the answer is Yes. If however, say, the filter criteria is: WHERE C = ‘X’ and B = 2 and A > getdate(), then we would scan a lot of C=’Y’ and B != 2 values while we are scanning the A > getdate() values (this criteria of A > the value that we used in the example below qualified nearly 200,000 records). A better thing would be to use B,C,A in that case to reach the place where B and C equality matches are done and scan the rest of the records using the A > getdate() criteria.

So, understand your queries and how you are going to be accessing the data and then decide how the composite index needs to be formed

Question 2: Is order of column always important:
Answer is :  If I have all the columns of the composite index in the filter condition (WHERE clause), then the order in which they are specified in the filter criteria does not matter.

So strategy could be start indexing columns from highly selective to least but consider cases of queries where there is no equality conditions also.

Another strategy if index skip scan has to be kept in mind is:

  1. If a and b both have 1000 distinct values and they are always queried together then the order of columns in the index doesn't really matter. But a has only 10 distinct values or you have queries which use just one of the columns then it does matter; in these scenarios the index may not be used if the column ordering does not suit the query.
  2. The column with the least distinct values ought to be first and the column with the most distinct values last. This not only maximises the utility of the index it also increases the potential gains from index compression.
  3. The datatype and length of the column have an impact on the return we can get from index compression but not on the best order of columns in an index.
  4. Arrange the columns with the least selective column first and the most selective column last. In the case of a tie lead with the column which is more likely to be used on its own.
The two main reasons for leading with the least selective column are
  1. Index compression
  2. Index Skip reads
Both these work their magic from knowing that the value in the current slot is the same as the value in the previous slot. Consequently we can maximize the return from these techniques by minimsing the number of times the value changes. In the following example, A has four distinct values and B has six. The dittos represent a compressible value or a skippable index block.
Least selective column leads ...

A          B
---------  -
AARDVARK   1
"          2
"          3
"          4
"          5
"          6
DIFFVAL    1
"          2
"          3
"          4
"          5
"          6
OTHERVAL   1
"          2
"          3
"          4
"          5
"          6
WHATEVER   1
"          2
"          3
"          4
"          5
"          6
Most selective column leads ...
B  A
-  --------
1  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
2  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
3  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
4  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
5  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
6  AARDVARK
"  DIFFVAL
"  OTHERVAL
"  WHATEVER
Even in this trival example, (A, B) has 20 skippable slots compared to the 18 of (B, A). A wider disparity would generate greater ROI on index compression or better utility from Index Skip reads.


Question 4:

I have a Composite Primary key which is combination of Column1, and Column2. What would be the difference if I have Composite key with Column2, and Column1. Does it even really matter which order you set columns?

It does, but perhaps not in the way you might expect.

Given col1 and col2, people's initial instinct is to put the more selective column first.

But, that's almost always irrelevant.

First, you need to consider how the index will be used in your SQL statements. If some SQLs only specify col1, then col1 should probably be first. If all SQLs will always specify both columns in the where predicate, then it doesn't matter which comes first.

But, what if most of the SQLs specify both col1 and col2, and a few provide only col1 or col2?

Well, in that case, the least selective column should (probably) go first. There are two reasons for this. First, if the least selective goes first, then, for those queries where only the second column is specified in the predicate, Oracle will be more likely to do an INDEX SKIP SCAN, if the leading column is not very selective. Second, by putting the less selective column first, you'll be more likely to be able to take advantage of index compression.


Choosing Composite Indexes


A composite index contains more than one key column. Composite indexes can provide additional advantages over single-column indexes:

  • Improved selectivity
    Sometimes two or more columns or expressions, each with poor selectivity, can be combined to form a composite index with higher selectivity.
  • Reduced I/O
    If all columns selected by a query are in a composite index, then Oracle can return these values from the index without accessing the table.

A SQL statement can use an access path involving a composite index if the statement contains constructs that use a leading portion of the index

A leading portion of an index is a set of one or more columns that were specified first and consecutively in the list of columns in the CREATE INDEX statement that created the index. Consider this CREATE INDEX statement:
CREATE INDEX comp_ind 
ON table1(x, y, z);

  • x, xy, and xyz combinations of columns are leading portions of the index
  • yz, y, and z combinations of columns are not leading portions of the index

Choosing Keys for Composite Indexes


Follow these guidelines for choosing keys for composite indexes:

  • Consider creating a composite index on keys that are used together frequently in WHERE clause conditions combined with AND operators, especially if their combined selectivity is better than the selectivity of either key individually.
  • If several queries select the same set of keys based on one or more key values, then consider creating a composite index containing all of these keys.

Of course, consider the guidelines associated with the general performance advantages and trade-offs of indexes described in the previous sections.

Ordering Keys for Composite Indexes


Follow these guidelines for ordering keys in composite indexes:

  • Create the index so the keys used in WHERE clauses make up a leading portion.
  • If some keys are used in WHERE clauses more frequently, then be sure to create the index so that the more frequently selected keys make up a leading portion to allow the statements that use only these keys to use the index.
  • If all keys are used in WHERE clauses equally often, then ordering these keys from most selective to least selective in the CREATE INDEX statement best improves query performance.
  • If all keys are used in the WHERE clauses equally often but the data is physically ordered on one of the keys, then place that key first in the composite index.
 References:
 http://decipherinfosys.wordpress.com/2008/05/13/column-order-in-a-composite-index/
 http://stackoverflow.com/questions/2196484/oracle-does-the-column-order-matter-in-an-index
http://docs.oracle.com/cd/B28359_01/server.111/b28274/sql_overview.htm

http://dba.stackexchange.com/questions/21017/does-order-matter-for-composite-primary-key-in-oracle-or-any-database

DBA TYPE: which one you fall in...

I noticed I had below on a edited but never published post one year ago:

... and I'm back again ...

and back again after more than two years

Cervical pain still running but I cannot resist temptation to write..

Which DBA you are :

Spartan DBA,
little bit (not much) Diplomatic DBA,
Scientific DBA,

or have all traits


Or say are you Nut Bolt DBA or are you Tool DBA.

Or say are you Development DBA or a Production DBA.

or are you Developer and DBA both.

It all depends what needs your organization and you.

I believe DBA shouuld have scientific approach and very much spartan to be brave.
Needs to know how to handle clients when client does not try to understand why DBA needs more time to work on issue or resources. A good DBA if is able to code is great combination. DBA should be familar with chgallenges of both DEV and Prod DB environments.





deciding execution plan when query is transformed.

Oracle 10g brought cost based query transformation. Consider two below cases and decide what plan of COST based optimizer is good.  Is transformation it is considering is good.

INDEX IDX_HIST_ID ON HISTDATA(ID)

exec :b1:=1

SELECT  /*+ use_nl (A B ) */  MAX (id)
                         FROM histdata A ,  sustained_feed b
                      WHERE A.id=b.id
                        AND b. data_bucket = :B1;

SELECT STATEMENT Optimizer Mode=ALL_ROWS 1   1779                    
  SORT AGGREGATE 1   10                    
    NESTED LOOPS 610 K 5 M 1779                    
      TABLE ACCESS FULL MT_RPT.sustained_feed 17   119   3                    
      FIRST ROW 35 K 105 K 104                    
        INDEX RANGE SCAN (MIN/MAX) MT_RPT.IDX_HIST_ID 35 K 105 K 104                    


SELECT    MAX (id)
                          FROM histdata
                         WHERE id IN (SELECT  id
                                                   FROM sustained_feed
                                                  WHERE data_bucket = :B1);


Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop

SELECT STATEMENT Optimizer Mode=ALL_ROWS 1   872                    
  SORT AGGREGATE 1   10                    
    HASH JOIN 610 K 5 M 872                    
      TABLE ACCESS FULL MT_RPT.sustained_feed 17   119   3                    
      INDEX FAST FULL SCAN MT_RPT.IDX_HIST_ID 933 K 2 M 759                

corrupt indexes key-rowid value mismatch

Short Circuit caused power Down=>SAN Down=>All databases servers using this storage  Crashed=>Oracle 9i DB started fine=>but…OOPS! SAP Application not working properly


Once a User complained about some modules having issues :
There was a Database Alert Log recorded ORA-600 internal error with different first actual code argument. A search into Meta link showed that DB had problems in executing DMLs on some tables, as some of  the indices on these tables had logical corruption. So it was thought, when DB crashed, DMLS on these tables were in progress,causing update of index entries. so BAD row mismatch occurred as key-row information was not consistent. So  queries and DMLs which would involve these indexes were failing.
Restart of the database, automatic Crash Recovery of Database[without intervention of DBA] was successful, there was no corruption shown in logs nor in any datafile block.  So when DB started,  SAN was made up again. After restoring power supply, it was opened successfully. But it had a logical corruption hidden inside bad leaf blocks of indices.  Automatic crash recovery was not able to fix index logical corruption. This is obvious behavior. Some  won’t expect such issue and blame RDBMS some won't. I won't.


Solution:


1. Identify such indices which are in bad shape and fix problematic indexes
a) drop and recreate such indexes  OR
b) rebuild them  online so that it read key values/rowed from table (normal rebuild  reads existing index entries so corruption does not go away)


2. Restore from backup and recover
The latter  was not feasible as DB had size 1TB and prod database was on RAID 5. It would  have taken more than 2 days to restore it!!!

Another case:
I seen some people killing their application had corrupt indexes. It means when they killed they had some running DMLs on tables.

oracle education content

Oracle has changed a lot the look and feel of its tutorials. They have changed links to reach viewlets. You need good at search. below is URL:

http://apex.oracle.com/pls/apex/f?p=44785:2:1734844480451698::NO:2,CIR,RIR:P2_PRODUCT_ID,P2_RELEASE_ID:2014,85



litle bit on jdbc & oracle when every milisecond matters

when every milisecond matters

1. use OCI JDBC driver , undoubetdly they are fastest JDBC driver , especially when there is lot of fetch. I found them performing 10% faster for a bulk fetch.

2. if java application has to fecth large data then use larger fetch size .

3. When DB server and Java application server are on same box, use OCI JDBC driver. In this case db connection will be using IPC and gives best performance. And ovbvious as states in 1. above , JDBC OCI are fastest even when used for remote connection to DB.

4. if using connection pooling,there should be good number of connections available otherwise there will be more opened cursors per session.

5. perform analysis for use of preparestatement vs statement. Don't go blindly for preparestatement

6. use batch update but just be wise to choose batch size. If keep it is small no benefit and if it is large again it will need more undo in database. Similarly guys when you add statements in batch be wise loop does not iterate very much other wise you will need good memory for your java program.

some tail info not related actually to performance:

-- use the JDBC drivers matching DB server jdbc drivers version. I seen people running  oracle 10.2.0.4 but their jdbc driver was version 10.2.0.1. They had issue in fetching from result set sometimes. Fix suggested was to use matching jdbc driver and it did .

--  if using connection pool,take extreme care to check result sets are closed properly and opened cursors does not exceed limit.




text indexes in comparison query ( tune like query using text index for large tables)

consider text comparison query:
select e.empno,e.ename,d.loc,d.dname from emp e ,dept d
where e.ename like '%'||d.dname||'%'

This is suitable case of use of text indexes if table emp is quite big.

steps:

1 .FROM DBA ---------

GRANT SELECT ON ctxsys.dr$ths_phrase to scott;
GRANT EXECUTE ON CTX_DDL TO scott;

FROM CTXSYS USER ----------------
exec ctx_ddl.create_stoplist('empty_stoplist', 'BASIC_STOPLIST');
exec ctx_ddl.create_preference('matching_lexer', 'BASIC_LEXER');

2.FROM application USER scott ------

CREATE INDEX emp_ename2 ON emp2 (ename) INDEXTYPE IS CTXSYS.CONTEXT parameters ('lexer CTXSYS.matching_lexer');

in order to take benefit of this text index rewrite above query as below :

select e.*
from emp2 e, dept d
where CONTAINS(e.ename, '%'||d.dname||'%')>0


***** Spool outputs along with execution plans *****

SYSTEM session:

1* alter user ctxsys identified by sys
SQL> /

User altered.

SQL> GRANT SELECT ON ctxsys.dr$ths_phrase to scott;

Grant succeeded.

SQL> GRANT EXECUTE ON CTX_DDL TO scott;

Grant succeeded.

SQL> exec ctx_ddl.create_stoplist('empty_stoplist', 'BASIC_STOPLIST');

PL/SQL procedure successfully completed.

SQL> exec ctx_ddl.create_preference('matching_lexer', 'BASIC_LEXER');

PL/SQL procedure successfully completed.


--SCOTT session --

SQL> create table emp2 as select * from emp;

Table created.

SQL> alter table emp2 modify empno number(7);

Table altered.

SQL> alter table emp2 modify ename varchar2(200);

Table altered.

SQL> create sequence s1 ;

Sequence created.

SQL> insert into emp2(empno,ename) select s1.nextval,'SALES' from emp2;

15 rows created.

insert repeated for

30720 rows created.

SQL> commit;

Commit complete.

SQL> insert into emp2(empno,ename) select s1.nextval,'ABCD SALES HOLA' from emp2 where rownum<=1000; 1000 rows created. SQL> select * from dept;

DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON

SQL> insert into emp2(empno,ename) select s1.nextval,'ABCDACCOUNTINGEXYZ' from emp2;

62440 rows created.

SQL> commit;

Commit complete.

SQL> CREATE INDEX emp_ename2 ON emp2 (ename) INDEXTYPE IS CTXSYS.CONTEXT parameters ('lexer CTXSYS.matching_lexer');

Index created.

SQL> set autotrace traceonly explain
SQL> select e.*
2 from emp2 e, dept d
3 where CONTAINS(e.ename, '%'||d.dname||'%')>0 ;

Execution Plan
----------------------------------------------------------
Plan hash value: 4225699595

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 224 | 45696 | 66 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 224 | 45696 | 66 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 40 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP2 | 56 | 10864 | 66 (0)| 00:00:01 |
|* 4 | DOMAIN INDEX | EMP_ENAME2 | | | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("CTXSYS"."CONTAINS"("E"."ENAME",'%'||"D"."DNAME"||'%')>0)

Note
-----
- dynamic sampling used for this statement (level=2)

SQL> insert into emp2(empno,ename) select s1.nextval,'bola' from emp2 ;

124880 rows created.

SQL> commit;

Commit complete.

SQL> set autotrace traceonly explain statistics timing on
SQL> select e.*
2 from emp2 e, dept d
3 where CONTAINS(e.ename, '%'||d.dname||'%')>0 ;

124866 rows selected.

Elapsed: 00:00:01.23

Execution Plan
----------------------------------------------------------
Plan hash value: 4225699595

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 682 | 135K| 146 (0)| 00:00:02 |
| 1 | NESTED LOOPS | | 682 | 135K| 146 (0)| 00:00:02 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 40 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP2 | 170 | 32980 | 146 (0)| 00:00:02 |
|* 4 | DOMAIN INDEX | EMP_ENAME2 | | | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("CTXSYS"."CONTAINS"("E"."ENAME",'%'||"D"."DNAME"||'%')>0)

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
750 recursive calls
0 db block gets
10354 consistent gets
5 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
26 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> /

124866 rows selected.

Elapsed: 00:00:00.84

Execution Plan
----------------------------------------------------------
Plan hash value: 4225699595

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 682 | 135K| 146 (0)| 00:00:02 |
| 1 | NESTED LOOPS | | 682 | 135K| 146 (0)| 00:00:02 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 40 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP2 | 170 | 32980 | 146 (0)| 00:00:02 |
|* 4 | DOMAIN INDEX | EMP_ENAME2 | | | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("CTXSYS"."CONTAINS"("E"."ENAME",'%'||"D"."DNAME"||'%')>0)

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
52 recursive calls
0 db block gets
9682 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> drop index EMP_ENAME2;

Index dropped.

Elapsed: 00:00:02.93
SQL> ed
Wrote file afiedt.buf

1 select e.*
2 from emp2 e, dept d
3* where e.ename like '%'||d.dname||'%'
SQL>
SQL> /

124866 rows selected.

Elapsed: 00:00:01.01

Execution Plan
----------------------------------------------------------
Plan hash value: 4088618096

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43131 | 8087K| 831 (2)| 00:00:10 |
| 1 | NESTED LOOPS | | 43131 | 8087K| 831 (2)| 00:00:10 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 40 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP2 | 10783 | 1916K| 207 (2)| 00:00:03 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("E"."ENAME" LIKE '%'||"D"."DNAME"||'%')

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
178 recursive calls
0 db block gets
11239 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> /

124866 rows selected.

Elapsed: 00:00:01.01

Execution Plan
----------------------------------------------------------
Plan hash value: 4088618096

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43131 | 8087K| 831 (2)| 00:00:10 |
| 1 | NESTED LOOPS | | 43131 | 8087K| 831 (2)| 00:00:10 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 40 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP2 | 10783 | 1916K| 207 (2)| 00:00:03 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("E"."ENAME" LIKE '%'||"D"."DNAME"||'%')

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
11157 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> insert into emp2(empno,ename) select s1.nextval,'z' from emp2;

249760 rows created.

Elapsed: 00:00:02.75


SQL> commit;

Commit complete.

Elapsed: 00:00:00.10
SQL> set autotrace off
SQL> insert into emp2(empno,ename) select s1.nextval,null from emp2;

499520 rows created.

Elapsed: 00:00:06.32
SQL> commit;

Commit complete.

Elapsed: 00:00:00.01
SQL> CREATE INDEX emp_ename2 ON emp2 (ename) INDEXTYPE IS CTXSYS.CONTEXT parameters ('lexer CTXSYS.matching_lexer');

Index created.

Elapsed: 00:00:22.06
SQL> select e.*
2 from emp2 e, dept d
3 where CONTAINS(e.ename, '%'||d.dname||'%')>0
4
SQL>
SQL> set autotrace traceonly explain statistics timing on
SQL>
SQL> /

124866 rows selected.

Elapsed: 00:00:00.86

Execution Plan
----------------------------------------------------------
Plan hash value: 4225699595

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1852 | 368K| 356 (0)| 00:00:05 |
| 1 | NESTED LOOPS | | 1852 | 368K| 356 (0)| 00:00:05 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 40 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP2 | 463 | 89822 | 356 (0)| 00:00:05 |
|* 4 | DOMAIN INDEX | EMP_ENAME2 | | | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("CTXSYS"."CONTAINS"("E"."ENAME",'%'||"D"."DNAME"||'%')>0)

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
392 recursive calls
0 db block gets
10671 consistent gets
2 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> select e.*
2 from emp2 e, dept d
3 where e.ename like '%'||d.dname||'%' ;

124866 rows selected.

Elapsed: 00:00:01.42

Execution Plan
----------------------------------------------------------
Plan hash value: 4088618096

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 185K| 33M| 2103 (3)| 00:00:26 |
| 1 | NESTED LOOPS | | 185K| 33M| 2103 (3)| 00:00:26 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 40 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP2 | 46308 | 8230K| 525 (3)| 00:00:07 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("E"."ENAME" LIKE '%'||"D"."DNAME"||'%')

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
15761 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> /

124866 rows selected.

Elapsed: 00:00:01.42

Execution Plan
----------------------------------------------------------
Plan hash value: 4088618096

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 185K| 33M| 2103 (3)| 00:00:26 |
| 1 | NESTED LOOPS | | 185K| 33M| 2103 (3)| 00:00:26 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 40 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP2 | 46308 | 8230K| 525 (3)| 00:00:07 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("E"."ENAME" LIKE '%'||"D"."DNAME"||'%')

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
15693 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
124866 rows processed

SQL> select e.*
2 from emp2 e, dept d
3 where CONTAINS(e.ename, '%'||d.dname||'%')>0;

124866 rows selected.

Elapsed: 00:00:00.82

Execution Plan
----------------------------------------------------------
Plan hash value: 4225699595

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1852 | 368K| 356 (0)| 00:00:05 |
| 1 | NESTED LOOPS | | 1852 | 368K| 356 (0)| 00:00:05 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 40 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP2 | 463 | 89822 | 356 (0)| 00:00:05 |
|* 4 | DOMAIN INDEX | EMP_ENAME2 | | | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("CTXSYS"."CONTAINS"("E"."ENAME",'%'||"D"."DNAME"||'%')>0)

Note
-----
- dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
52 recursive calls
0 db block gets
10242 consistent gets
0 physical reads
0 redo size
2295427 bytes sent via SQL*Net to client
91959 bytes received via SQL*Net from client
8326 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
124866 rows processed