Saturday, January 2, 2010

Performance Tuning tools

There are some mandatory tools that every should know atleast to think about performance tuning in production environment.
These tools, i say are equivalent to a 'stethoscope' for a doctor or a bat for a cricket player for that matter with out which you cant do anything.
1. sql* plus
2. explain plan
3. auto trace
4. sql trace with TKPROF
5. statspack

SQL*PLUS
1. sql* plus is a good scripting tool.

4. SQL trace with TKPROF
sql_trace and tkprof are the tools used to tune queries.
sql_trace generally provides raw data about the SQL queries that your application used and its performance statistics.
tkprof is a tool which formats the raw data provided by the sql_trace to readable format.

In general, TKPROF output provides the statistics of the query, waiting events, query plan and bind variable values.
There is a difference between the query plan generated between through TKPROF and EXPLAN PLAN. Infact, TKPROF provides the actual plan that is used where as the EXPLAN PLAN query plan changes based on the session settings. It shows the query plan as if we execute the query right now. So, TKPROF query plan is the acutal one used and is the one reliable.

There are some mandatory steps that needs to be performed to enable SQL_TRACE.

1. alter session set timed_statistics=true;
2. alter session set events '1024 trace name context forever, level 12';


timed_statistics is a parameter which tells the server to collect the timed activities (cpu time, elapsed time etc) for various activities in the database.

timed_statistics can be set at the session
Alter session set timed_statistics=true;
or system level.
Alter system set timed_statistics=true;
or global level.
For global level, the init.ora file should have the following entry
timed_statistics=true
To take this effect the database needs to be restarted.

sql_trace can also be enabled at the system or session level or global level.
sql_trace produces lot of data. It accepts either true or false values. The setting for global level can be done at init.ora file.
USER_DUMP_DEST in init.ora file shows the path for the rawdata.
MAX_DUMP_FILE_SIZE is an another parameter for this regard. It controls the max size of the file generated by the sql_trace.
If a numeric value is assigned, then it takes the value as the db blocks.
It can also assigned using K or M bytes.
We can also give UNLIMITED for this parameter. But ingeneral it is not adviced to give unlimited as it loads lots of data with it. A max size of 50 or 100MB is advicable.

The ways to enable sql_trace:
1. Alter session set sql_trace=true|false this is equivalent to
alter session set events '1024 trace name context forever,level 1' so we can use eiether of it
There are certain advantages if we go for level 4,8 and 12
level 4: enables sql_trace and also captures the bind variable values in the trace file
level 8: enables sql_trace and also captures wait events in the trace file
level 12: enables sql_trace and also captures wait events and bind variable values.

The overall syntax is as follows..
alter session set events ‘10046 trace name context forever, level
alter session set events ‘10046 trace name context off’--to stop tracing

2. sys.dbms_system.set_sql_trace_in_session: This allows to set sql_trace on or off for the existing session. All needs to get is the session id and serial# for the session which can be obtained from the v$session.


The trace file generated contains the session’s data irrespective of the query.
So we must close the session or stop tracing after the needed query processed but before that we need to identify where the trace file is located.

The following block gives the process server id:
Trace file contains the server id in the file name

SELECT p.spid
FROM v$process p, v$session s
WHERE p.addr = s.paddr AND s.audsid = USERENV ('sessionid')

The following blocks gives the full file path of the folder where trace fiels are sits.

declare
l_intval number;
l_strval varchar2(2000);
l_type number;
begin
l_type := dbms_utility.get_parameter_value
('user_dump_dest', l_intval, l_strval);
dbms_output.put_line(l_strval );
end;
/

By making use of the above two queries we can find out the trace file.

Now we are done with generation of trace file and finding the path for it.
But how to read the trace file? TKPROF is a tool used to rearrange the trace file contents in a readble fashion.

To read the trace file we need to have the privileges that are given by
_trace_files_public = true

In command line we need to give,
tkprof inputfilename(.trc) outputfilename(.txt)
Thats all now we have reable trace file at the outputfilename(.txt)

Here are the steps that i did to generate a trace file

SQL> alter session set timed_statistics=true
2 ;
Session altered.
SQL> alter session set sql_trace=true
2 ;
Session altered.
SQL> select owner,count(*) from dba_objects group by owner;
SQL> select p.spid from v$process p, v$session s where p.addr=s.paddr and s.audsid=userenv('sessionid')
/
SPID
------------
3916

Friday, January 1, 2010

Clustter Table

A cluster is a schema object that contains one or more tables that all have one or more columns in common. Rows of one or more tables that share the same value in these common columns are physically stored together within the database.

Generally, you should only cluster tables that are frequently joined on the cluster key columns in SQL statements. Clustering multiple tables improves the performance of joins, but it is likely to reduce the performance of full table scans, INSERT statements, and UPDATE statements that modify cluster key values. Before clustering, consider its

Cluster Keys
The columns defined by the CREATE CLUSTER command make up the cluster key. These cluster columns must correspond in both datatype and size to columns in each of the clustered tables, although they need not correspond in name.

Types of Clusters
A cluster can be either an indexed cluster or a hash cluster.

Indexed Clusters
In an indexed cluster, Oracle stores together rows having the same cluster key value. Each distinct cluster key value is stored only once in each data block, regardless of the number of tables and rows in which it occurs. This saves disk space and improves performance for many operations.

You may want to use indexed clusters in the following cases:

·Your queries retrieve rows over a range of cluster key values.

·Your clustered tables may grow unpredictably.

After you create an indexed cluster, you must create an index on the cluster key before you can issue any data manipulation language (DML) statements against a table in the cluster. This index is called the cluster index.

A cluster index provides quick access to rows within a cluster based on the cluster key. If you issue a SQL statement that searches for a row in the cluster based on its cluster key value, Oracle searches the cluster index for the cluster key value and then locates the row in the cluster based on its ROWID.

Hash Clusters
In a hash cluster, Oracle stores together rows that have the same hash key value. The hash value for a row is the value returned by the cluster's hash function. When you create a hash cluster, you can either specify a hash function or use the Oracle internal hash function. Hash values are not actually stored in the cluster, although cluster key values are stored for every row in the cluster.

You may want to use hash clusters in the following cases:

·Your queries retrieve rows based on equality conditions involving all cluster key columns.

·Your clustered tables are static or you can determine the maximum number of rows and the maximum amount of space required by the cluster when you create the cluster.

The hash function provides access to rows in the table based on the cluster key value. If you issue a SQL statement that locates a row in the cluster based on its cluster key value, Oracle applies the hash function to the given cluster key value and uses the resulting hash value to locate the matching rows. Because multiple cluster key values can map to the same hash value, Oracle must also check the row's cluster key value. This process often results in less I/O than the process for the indexed cluster, because the index search is not required.

Oracle's internal hash function returns values ranging from 0 to the value of HASHKEYS - 1. If you specify a column with the HASH IS clause, the column values need not fall into this range. Oracle divides the column value by the HASHKEYS value and uses the remainder as the hash value. The hash value for null is HASHKEYS - 1. Oracle also rounds the HASHKEYS value up to the nearest prime number to obtain the actual number of hash values. This rounding reduces the likelihood of hash collisions, or multiple cluster key values having the same hash value.

You cannot create a cluster index for a hash cluster, and you need not create an index on a hash cluster key.

If you cannot fit all rows for one hash value into a data block, do not use hash clusters. Performance is very poor in this circumstance because an insert or update of a row in a hash cluster with a size exceeding the data block size fills the block and performs row chaining to contain the rest of the row.

Cluster Size
Oracle uses the value of the SIZE parameter to determine the space reserved for rows corresponding to one cluster key value or one hash value. This space then determines the maximum number of cluster or hash values stored in a data block. If the SIZE value is not a divisor of the data block size, Oracle uses the next largest divisor. If the SIZE value is larger than the data block size, Oracle uses the operating system block size, reserving at least one data block per cluster or hash value.

Oracle also considers the length of the cluster key when determining how much space to reserve for the rows having a cluster key value. Larger cluster keys require larger sizes. To see the actual size, query the KEY_SIZE column of the USER_CLUSTERS data dictionary view. This does not apply to hash clusters because hash values are not actually stored in the cluster.

Although the maximum number of cluster and hash key values per data block is fixed on a per `-cluster basis, Oracle does not reserve an equal amount of space for each cluster or hash key value. Varying this space stores data more efficiently, because the data stored per cluster or hash key value is rarely fixed.

A SIZE value smaller than the space needed by the average cluster or hash key value may require the data for one cluster key or hash key value to occupy multiple data blocks. A SIZE value much larger results in wasted space.

When you create a hash cluster, Oracle immediately allocates space for the cluster based on the values of the SIZE and HASHKEYS parameters.



Adding Tables to a Cluster
You can add tables to an existing cluster by issuing a CREATE TABLE statement with the CLUSTER clause. A cluster can contain as many as 32 tables, although the performance gains of clustering are often lost in clusters of more than four or five tables.

All tables in the cluster have the cluster's storage characteristics as specified by the PCTUSED, PCTFREE, INITRANS, MAXTRANS, TABLESPACE, and STORAGE parameters.

Example I
The following statement creates an indexed cluster named PERSONNEL with the cluster key column DEPARTMENT_NUMBER, a cluster size of 512 bytes, and storage parameter values:

CREATE CLUSTER personnel

( department_number NUMBER(2) )

SIZE 512

STORAGE (INITIAL 100K NEXT 50K PCTINCREASE 10);

The following statements add the EMP and DEPT tables to the cluster:

CREATE TABLE emp

(empno NUMBER PRIMARY KEY,

ename VARCHAR2(10) NOT NULL

CHECK (ename = UPPER(ename)),

job VARCHAR2(9),

mgr NUMBER REFERENCES scott.emp(empno),

hiredate DATE CHECK (hiredate >= SYSDATE),

sal NUMBER(10,2) CHECK (sal > 500),

comm NUMBER(9,0) DEFAULT NULL,

deptno NUMBER(2) NOT NULL )

CLUSTER personnel (deptno);

CREATE TABLE dept

(deptno NUMBER(2),

dname VARCHAR2(9),

loc VARCHAR2(9))

CLUSTER personnel (deptno);

The following statement creates the cluster index on the cluster key of PERSONNEL:

CREATE INDEX idx_personnel ON CLUSTER personnel;

After creating the cluster index, you can insert rows into either the EMP or DEPT tables.

Example II
The following statement creates a hash cluster named PERSONNEL with the cluster key column DEPARTMENT_NUMBER, a maximum of 503 hash key values, each of size 512 bytes, and storage parameter values:

CREATE CLUSTER personnel

( department_number NUMBER )

SIZE 512 HASHKEYS 500

STORAGE (INITIAL 100K NEXT 50K PCTINCREASE 10);


Because the above statement omits the HASH IS clause, Oracle uses the internal hash function for the cluster.

Example III
The following statement creates a hash cluster named PERSONNEL with the cluster key made up of the columns HOME_AREA_CODE and HOME_PREFIX, and uses a SQL expression containing these columns for the hash function:

CREATE CLUSTER personnel

( home_area_code NUMBER,

home_prefix NUMBER )

HASHKEYS 20

HASH IS MOD(home_area_code + home_prefix, 101);

Quoting string literals in 10g

Quoting string literals in 10g

This short article introduces Oracle's new quoting mechanism in PL/SQL. This is a new feature of 10g that enables us to embed single-quotes in literal strings without having to resort to double, triple or sometimes quadruple quote characters. This is particularly useful for building dynamic SQL statements that contain quoted literals.

The mechanism is invoked with a simple "q" in PL/SQL only. The syntax is q'[...]', where the "[" and "]" characters can be any of the following as long as they do not already appear in the string.

!
[ ]
{ }
( )
< >
Note that at the time of writing, the quoting mechanism only appears to work with 10g clients/OCI. If used with any software that uses an older Oracle client it fails with ORA-01756: quoted string not properly terminated (confirmed with sqlplus, TOAD and PL/SQL Developer).

a simple example

The following example demonstrates how simple it is to use this mechanism when building a string that contains single quotes.

SQL> DECLARE
2 v VARCHAR2(1024);
3 BEGIN
4 v := q'[It's a string with embedded quotes...]';
5 DBMS_OUTPUT.PUT_LINE(v);
6 END;
7 /
It's a string with embedded quotes...

PL/SQL procedure successfully completed.
using the quoting mechanism in dynamic sql

Many developers will be familiar with dynamic SQL. While providing a flexible solution to many programming problems, dynamic SQL can also be difficult to build, debug and support. The quoting mechanism doesn't make these problems go away, but it at least makes dynamic SQL a little bit easier to transfer between SQL editors and packages during development.

The following is a contrived example of a dynamic SQL statement that includes literal quotes in the string. The sharp-witted amongst us will notice that this statement doesn't in fact need to be dynamic (and even if it did, it should be using bind variables). However, in the interest of demonstrating the quoting mechanism in as simple and short an example as possible, we'll stick with it.

SQL> DECLARE
2 v_sql VARCHAR2(1024);
3 v_cnt PLS_INTEGER;
4 BEGIN
5 v_sql := q'[SELECT COUNT(*) FROM user_objects WHERE object_type = 'TABLE']';
6 EXECUTE IMMEDIATE v_sql INTO v_cnt;
7 DBMS_OUTPUT.PUT_LINE(
8 TO_CHAR(v_cnt) || ' tables in USER_OBJECTS.'
9 );
10 END;
11 /
4 tables in USER_OBJECTS.

PL/SQL procedure successfully completed.

Friday, December 25, 2009

How to get DDL for DB objects and Dependent Objects

It was very tough to get the DDL for database objects in eralier versions of Oracle. But from Oracle 9i, it is pretty easy to get the DDL of Database Objects.

In Oracle9i we have the exciting new dbms_metadata utility to display DDL directly from the data dictionary. Using this powerful utility, we can punch individual objects or an entire schema.

Best of all, it is easy. You simply execute dbms_metadata. get_ddl.

To punch off all table and indexes for the EMP table, we execute dbms_metadata. get_ddl, select from DUAL, and providing all required parameters.


set heading off;
set echo off;
Set pages 999;
set long 90000;

spool ddl_list.sql

select dbms_metadata.get_ddl('TABLE','DEPT','SCOTT') from dual;

select dbms_metadata.get_ddl('INDEX','DEPT_IDX','SCOTT') from dual;

spool off;

Here is the output. The only thing missing is the ending semicolons after each statement. Just for illustration, we show how a primary key can be punched as part of the table DDL or separately using the INDEX argument.

CREATE TABLE "SCOTT"."DEPT"
( "DEPTNO" NUMBER(2,0),
"DNAME" VARCHAR2(14),
"LOC" VARCHAR2(13),
CONSTRAINT "PK_DEPT" PRIMARY KEY ("DEPTNO")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 12288 NEXT 12288 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 50
FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ENABLE
) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING
STORAGE(INITIAL 12288 NEXT 12288 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 50
FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM"


CREATE UNIQUE INDEX "SCOTT"."DEPT_IDX" ON "SCOTT"."DEPT" ("DNAME")
FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM"

Now we can modify the syntax to punch a whole schema. It us easily done by selecting dbms_metadata. get_ddl and specifying USER_TABLES and USER_INDEXES. :

set pagesize 0
set long 90000
set feedback off
set echo off

spool scott_schema.sql

connect scott/tiger;

SELECT DBMS_METADATA.GET_DDL('TABLE',u.table_name)
FROM USER_TABLES u;

SELECT DBMS_METADATA.GET_DDL('INDEX',u.index_name)
FROM USER_INDEXES u;

spool off;

If the specified objectname is not found, this packaged procedure throws an error.

e.g.

SQL> SELECT dbms_metadata.get_dependent_ddl('INDEX','T1','SCOTT') FROM dual;
ERROR:
ORA-31608: specified object of type INDEX not found
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 86
ORA-06512: at "SYS.DBMS_METADATA", line 2808
ORA-06512: at "SYS.DBMS_METADATA", line 4394
ORA-06512: at line 1


Note: Above all we need to use 'select' query to get the result rather 'execute'
all the parameters passing to the packged procedure are case sensitive in general in upper case
Note: Make sure you give proper sql*plus session settings like
set wrap on
column column_name a5000
set trimspool on
set echo off
set long 6000

The below are the object types that are supported.
OBJECT TYPES
----------------
ASSOCIATION
AUDIT
AUDIT_OBJ
CLUSTER
COMMENT
CONSTRAINT
CONTEXT
DB_LINK
DEFAULT_ROLE
DIMENSION
DIRECTORY
FUNCTION
INDEX
INDEXTYPE
JAVA_SOURCE
LIBRARY
MATERIALIZED_VIEW
MATERIALIZED_VIEW_LOG
OBJECT_GRANT
OPERATOR
OUTLINE
PACKAGE
PACKAGE_SPEC
PACKAGE_BODY
PROCEDURE
PROFILE
PROXY
REF_CONSTRAINT
ROLE
ROLE_GRANT
ROLLBACK_SEGMENT
SEQUENCE
SYNONYM
SYSTEM_GRANT
TABLE
TABLESPACE
TABLESPACE_QUOTA
TRIGGER
TRUSTED_DB_LINK
TYPE
TYPE_SPEC
TYPE_BODY
USER
VIEW


1. An Object types you can see from querying the DBA_OBJECTS view for object_type.
2. A predefined type supplied by Oracle for use in the DBMS_METADATA functions such as REF_CONSTRAINT, OBJECT_GRANT, etc.




DBMS_METADATA.GET_DEPENDENT_DDL:
Often times we need to determine how a particular object relates to other objects in our database. This is what the GET_DEPENDENT_DDL function was created for. With this function it is easy to determine such things as the grants on an object, the referential integrity between objects, and the indexes on an object. This function can be a bit confusing when supplying inputs to the function call. What is needed to remember is that your are asking for items that are dependent on a named object in Oracle. Here are a few examples to help you get familiar with the proper use of this powerful function.

Suppose you wanted to extract all source code for triggers where a particular table was used in? You no longer need to search through DBA_SOURCE you need only issue the following SQL. Please note that it is the object_type that is dependent on the named object . Extend this query a bit and you can see how you might be able to extract all source code for an object with a simple changing of TRIGGER to include PROCEDURE, PACKAGE, FUNCTION, and JAVA_SOURCE.

e.g.

SQL> SELECT dbms_metadata.get_dependent_ddl('INDEX','T','SCOTT') FROM dual;

CREATE INDEX "SCOTT"."I_2" ON "SCOTT"."T" ("LNAME")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "USERS"

CREATE INDEX "SCOTT"."T_I" ON "SCOTT"."T" ("FNAME")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "USERS"



'CONSTRAINT' and 'REF_CONSTRAINT' key words can be used only with get_dependent_ddl but not with get_ddl.

There are two constraints and one ref constraints defined on the table T4. If 'CONSTRAINT' is used then we see only the defination of two constraints but not the refconstraints. The same with 'REF_CONSTRAINT'.
Below are the examples.

SQL> SELECT DBMS_METADATA.GET_DEPENDENT_DDL('CONSTRAINT','T4','SCOTT') from dua
l;

ALTER TABLE "SCOTT"."T4" ADD PRIMARY KEY ("A")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "USERS" ENABLE

ALTER TABLE "SCOTT"."T4" ADD UNIQUE ("B")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "USERS" ENABLE



Elapsed: 00:00:00.17
SQL> SELECT DBMS_METADATA.GET_DEPENDENT_DDL('REF_CONSTRAINT','T4','SCOTT') from
dual;

ALTER TABLE "SCOTT"."T4" ADD FOREIGN KEY ("C")
REFERENCES "SCOTT"."T6" ("A") ENABLE



Elapsed: 00:00:00.12
SQL> SELECT DBMS_METADATA.GET_DDL('REF_CONSTRAINT','T4','SCOTT') from dual;
ERROR:
ORA-31603: object "T4" of type REF_CONSTRAINT not found in schema "SCOTT"
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 105
ORA-06512: at "SYS.DBMS_METADATA", line 2805
ORA-06512: at "SYS.DBMS_METADATA", line 4333
ORA-06512: at line 1



no rows selected

Elapsed: 00:00:00.12
SQL> SELECT DBMS_METADATA.GET_DDL('CONSTRAINT','T4','SCOTT') from dual;
ERROR:
ORA-31603: object "T4" of type CONSTRAINT not found in schema "SCOTT"
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 105
ORA-06512: at "SYS.DBMS_METADATA", line 2805
ORA-06512: at "SYS.DBMS_METADATA", line 4333
ORA-06512: at line 1



DBMS_METADATA.GET_GRANTED_DDL
The purpose of the GET_GRANTED_DDL function is to generate the DDL required around grants given to a particular user.

If you wanted to generate the system grants given to a user.
select DBMS_METADATA.GET_GRANTED_DDL('SYSTEM_GRANT','') from dual;

If you wanted to generate the roles granted to a user.
select DBMS_METADATA.GET_GRANTED_DDL('ROLE_GRANT','') from dual;

If you wanted all the object grants given to a user.
select DBMS_METADATA.GET_GRANTED_DDL('OBJECT_GRANT','') from dual;

Sunday, December 13, 2009

In an interview i was asked to explain something about 'PRAGMA' keyword. I had no answer..here is the brief of it.

Pragma is a keyword in Oracle PL/SQL that is used to provide an instruction to the compiler.
The instruction is a statement that provides some instructions to the compiler.
Pragmas are defined in the declarative section in PL/SQL.

The following pragmas are available:

AUTONOMOUS_TRANSACTION:
Prior to Oracle 8.1, each Oracle session in PL/SQL could have at most one active transaction at a given time. In other words, changes were all or nothing. Oracle8i PL/SQL addresses that short comings with the AUTONOMOUS_TRANSACTION pragma. This pragma can perform an autonomous transaction within a PL/SQL block between a BEGIN and END statement without affecting the entire transaction. For instance, if rollback or commit needs to take place within the block without effective the transaction outside the block, this type of pragma can be used.

EXCEPTION_INIT:
The most commonly used pragma, this is used to bind a user defined exception to a particular error number.

For example:

Declare
I_GIVE_UP EXCEPTION;
PRAGMA EXCEPTION_INIT(I_give_up, -20000);

BEGIN
..

EXCEPTION WHEN I_GIVE_UP
do something..

END;

RESTRICT_REFERENCES:
Defines the purity level of a packaged program. This is not required starting with Oracle8i.

Prior to Oracle8i if you were to invoke a function within a package specification from a SQL statement, you would have to provide a RESTRICT_REFERENCE directive to the PL/SQL engine for that function. This pragma confirms to Oracle database that the function as the specified side-effects or ensures that it lacks any such side-effects.

Usage is as follows:

PRAGMA RESTRICT_REFERENCES(function_name, WNDS [, WNPS] [, RNDS], [, RNPS])

WNDS: Writes No Database State. States that the function will not perform any DMLs.

WNPS: Writes No Package State. States that the function will not modify any Package variables.

RNDS: Reads No Database State. Analogous to Write. This pragma affirms that the function will not read any database tables.

RNPS: Reads No Package State. Analogous to Write. This pragma affirms that the function will not read any package variables.

SERIALLY_REUSABLE:
This pragma lets the PL/SQL engine know that package-level data should not persist between reference to that data.

Package data (global variables in package specification etc.) by default persists for an entire session (or until a package is recompiled). Globally accessible data structures can cause some side effects. For instance, what if a cursor is left open in a package. In addition, a program can use up lots of real memory (UGA) and then not release it if the data is stored in a package-level structure.

In order to manage this, Oracle8i introduced the SERIALLY_REUSABLE pragma. This pragma is used in packages only and must be defined BOTH in specification and in the body.

The advantage is that based on the pragma, a package state can be reduced to a single call of a program unit in the package as opposed to the package being available for the whole session.

Decode and NULL

As a rule, comparison to NULL should always return NULL. However, DECODE is an exception as it evaluates NULL == NULL

SQL> SELECT decode(null,null,1,0) FROM dual;
DECODE(NULL,NULL,1,0)
---------------------
1

Sunday, December 6, 2009

Hierarchical Queries

If a table contains hierarchical data, then you can select rows in a hierarchical order using the hierarchical query clause:

START WITH specifies the root row(s) of the hierarchy.
CONNECT BY specifies the relationship between parent rows and child rows of the hierarchy.

• The NOCYCLE parameter instructs Oracle Database to return rows from a query even if a CONNECT BY LOOP exists in the data. Use this parameter along with the CONNECT_BY_ISCYCLE pseudocolumn to see which rows contain the loop
• In a hierarchical query, one expression in condition must be qualified with the PRIOR operator to refer to the parent row. For example,
• ... PRIOR expr = expr
• or
• ... expr = PRIOR expr


If the CONNECT BY condition is compound, then only one condition requires the PRIOR operator, although you can have multiple PRIOR conditions. For example:
CONNECT BY last_name != 'King' AND PRIOR employee_id = manager_id ...
CONNECT BY PRIOR employee_id = manager_id and
PRIOR account_mgr_id = customer_id ...


PRIOR is a unary operator and has the same precedence as the unary + and - arithmetic operators. It evaluates the immediately following expression for the parent row of the current row in a hierarchical query.

PRIOR is most commonly used when comparing column values with the equality operator. (The PRIOR keyword can be on either side of the operator.) PRIOR causes Oracle to use the value of the parent row in the column. Operators other than the equal sign (=) are theoretically possible in CONNECT BY clauses. However, the conditions created by these other operators can result in an infinite loop through the possible combinations. In this case Oracle detects the loop at run time and returns an error.
Oracle processes hierarchical queries as follows:

• A join, if present, is evaluated first, whether the join is specified in the FROM clause or with WHERE clause predicates.
• The CONNECT BY condition is evaluated.
• Any remaining WHERE clause predicates are evaluated.

Oracle then uses the information from these evaluations to form the hierarchy using the following steps:
1. Oracle selects the root row(s) of the hierarchy--those rows that satisfy the START WITH condition.
2. Oracle selects the child rows of each root row. Each child row must satisfy the condition of the CONNECT BY condition with respect to one of the root rows.
3. Oracle selects successive generations of child rows. Oracle first selects the children of the rows returned in step 2, and then the children of those children, and so on. Oracle always selects children by evaluating the CONNECT BY condition with respect to a current parent row.
4. If the query contains a WHERE clause without a join, then Oracle eliminates all rows from the hierarchy that do not satisfy the condition of the WHERE clause.

Oracle evaluates this condition for each row individually, rather than removing all the children of a row that does not satisfy the condition.

In hierarchical queries the syntax is as follows,
SELECT EMP.*,sys_connect_by_path(ename,'/'),LEVEL,connect_by_iscycle,connect_by_isleaf,CONNECT_BY_ROOT ename
FROM EMP
CONNECT BY NOCYCLE PRIOR empno=mgr
ORDER siblings BY ename


sys_connect_by_path(ename,'/'):
SYS_CONNECT_BY_PATH is valid only in hierarchical queries. It returns the path of a column value from root to node, with column values separated by char for each row returned by CONNECT BY condition.
Both column and char can be any of the datatypes CHAR, VARCHAR2, NCHAR, or NVARCHAR2. The string returned is of VARCHAR2 datatype and is in the same character set as column.

connect_by_iscycle:
The CONNECT_BY_ISCYCLE pseudocolumn returns 1 if the current row has a child which is also its ancestor. Otherwise it returns 0.
You can specify CONNECT_BY_ISCYCLE only if you have specified the NOCYCLE parameter of the CONNECT BY clause. NOCYCLE enables Oracle to return the results of a query that would otherwise fail because of aCONNECT BY loop in the data.

connect_by_isleaf:
The CONNECT_BY_ISLEAF pseudocolumn returns 1 if the current row is a leaf of the tree defined by the CONNECT BY condition. Otherwise it returns 0. This information indicates whether a given row can be further expanded to show more of the hierarchy.

Level:
For each row returned by a hierarchical query, the LEVEL pseudocolumn returns 1 for a root row, 2 for a child of a root, and so on. A root row is the highest row within an inverted tree. A child row is any nonroot row. A parent row is any row that has children. A leaf row is any row without children

Prior:
In a hierarchical query, one expression in the CONNECT BY condition must be qualified by the PRIOR operator. If the CONNECT BY condition is compound, then only one condition requires the PRIOR operator, although you can have multiple PRIOR conditions. PRIOR evaluates the immediately following expression for the parent row of the current row in a hierarchical query.
PRIOR is most commonly used when comparing column values with the equality operator. (The PRIOR keyword can be on either side of the operator.) PRIOR causes Oracle to use the value of the parent row in the column. Operators other than the equal sign (=) are theoretically possible in CONNECT BY clauses. However, the conditions created by these other operators can result in an infinite loop through the possible combinations. In this case Oracle detects the loop at run time and returns an error. Please refer to “Hierarchical Queries” for more information on this operator, including examples.

connect_by_root:
CONNECT_BY_ROOT is a unary operator that is valid only in hierarchical queries. When you qualify a column with this operator, Oracle returns the column value using data from the root row. This operator extends the functionality of the CONNECT BY [PRIOR] condition of hierarchical queries.
Restriction on CONNECT_BY_ROOT You cannot specify this operator in the START WITH condition or the CONNECT BY condition

siblings:
The SIBLINGS keyword is valid only if you also specify the hierarchical_query_clause (CONNECT BY). ORDER SIBLINGS BY preserves any ordering specified in the hierarchical query clause and then applies theorder_by_clause to the siblings of the hierarchy

Note:
1. In hierarchical queries where clause (not joins) is executed after the connect by clause.
2. Level can be used in the where clause of hierarchical query.
3. Where cant be placed after the connect by clause.

Here is a sample query which includes all the keywords

select empno, sys_connect_by_path(ename,'/') path, connect_by_root(ename), connect_by_isleaf, connect_by_iscycle
from emp
start with mgr is null
connect by nocycle prior empno = mgr
order siblings by ename
/

The simple implementation of hierarchical query is as followed by,
ORA11GR1> select * from counter where mgr=0;

X Y MGR
---------- ---------- ----------
1 A 0


It then applies the connect by TO EACH RECORD RETURNED BY THE START WITH. So, once this start with record is "output", it becomes the "prior" record. So, we take "X" from this prior record
ORA11GR1> select x from counter where mgr = 0;

X
----------
1



and use that value to retrieve the next level of records by MGR:
ORA11GR1> select * from counter where (select x from counter where mgr=0) = mgr;

X Y MGR
---------- ---------- ----------
2 B 1
3 C 1


so, that start with record is connected to these two records. Now, they each in turn become the "prior" record, so we'll take X=2 to find any MGR=2 records and output them (whence they themselves become the prior record and so on)

resulting in the hierarchy:
ORA11GR1> SELECT rpad('*',2*level,'*') data, x,y,mgr,LEVEL
2 FROM (SELECT x,y,mgr FROM counter )
3 START WITH mgr=0 CONNECT BY PRIOR x=mgr
4 ORDER BY mgr
5 /

DATA X Y MGR LEVEL
---------- ---------- ---------- ---------- ----------
** 1 A 0 1
**** 2 B 1 2
**** 3 C 1 2
****** 4 D 2 3
****** 6 F 2 3
****** 5 E 3 3
******** 7 G 4 4
******** 8 H 6 4

8 rows selected.


one level=1 (start with)
connected to the two records such that their MGR = level 1's X=1 value.


If you leave off the start with, that is like saying "start with 1=1, EVERY record". So every record is a level one record (and maybe someone elses level 2, 3, 4, whatever....)
ORA11GR1> SELECT rpad('*',2*level,'*') data, x,y,mgr,LEVEL
2 FROM (SELECT x,y,mgr FROM counter )
3 START WITH 1=1 CONNECT BY PRIOR x=mgr
4 ORDER BY mgr
5 /

DATA X Y MGR LEVEL
---------- ---------- ---------- ---------- ----------
** 1 A 0 1
**** 2 B 1 2
**** 3 C 1 2
** 2 B 1 1
** 3 C 1 1
**** 4 D 2 2
****** 6 F 2 3
** 6 F 2 1
**** 6 F 2 2
** 4 D 2 1
****** 4 D 2 3
****** 5 E 3 3
**** 5 E 3 2
** 5 E 3 1
******** 7 G 4 4
****** 7 G 4 3
** 7 G 4 1
**** 7 G 4 2
**** 8 H 6 2
** 8 H 6 1
******** 8 H 6 4
****** 8 H 6 3

22 rows selected.