Oracle Illustrated


September 16, 2010  2:56 AM

Generic DML Error logging

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

Create couple of transaction tables and one error log table.

Create table emp1 (id number, dept_no number, salary number)

create table dept1 (id number, name varchar2(10))

Create a generic error logging table -

create table error_log
    (ora_err_number$ number,
     ora_err_mesg$ varchar2(2000),
     ora_err_rowid$ rowid,
     ora_err_optyp$ varchar2(2),
     ora_err_tag$ varchar2(2000),
     ID    NUMBER
    )
/

We know about ORA_ERR_NUMBER$, ORA_ERR_MESG$, ORA_ERR_ROWID$ (will be populated only for update / delete), ORA_ERR_OPTYP$, ORA_ERR_TAG$ – These are mandatory columns for any DML error logging table. Note, I have created a column called ID which is common to EMP1 and DEPT1 table.

Now, lets try to perform INSERT into EMP1 and DEPT1 tables. Here I am using multi table inserts just for the sake of it.

INSERT ALL
    WHEN (1 = 1) THEN
    INTO emp1 (id, dept_no, salary)
    VALUES (empid, depid, ‘a’)
         LOG ERRORS INTO error_log (‘While inserting into emp1 table’) REJECT LIMIT UNLIMITED
    WHEN (1 = 1) THEN
    INTO dept1 (id, name)
    VALUES (depid, name)
         LOG ERRORS INTO error_log (‘While inserting into dept1 table’) REJECT LIMIT UNLIMITED    SELECT object_id empid,
           rownum depid,
           object_name name,
    object_id salary FROM all_objects where rownum < 50   
/

Query ERROR_LOG table -

 

ORA_ERR_NUMBER$ ORA_ERR_MESG$ ORA_ERR_ROWID$ ORA_ERR_OPTYP$ ORA_ERR_TAG$ ID
1722 ORA-01722: invalid number   I While inserting into emp1 table 367
1722 ORA-01722: invalid number   I While inserting into emp1 table 369
1722 ORA-01722: invalid number   I While inserting into emp1 table 370
1722 ORA-01722: invalid number   I While inserting into emp1 table 372
12899 ORA-12899: value too large for column “RFD_1FTEST”.”DEPT1″.”NAME” (actual: 20, maximum: 10)   I While inserting into dept1 table 7
12899 ORA-12899: value too large for column “RFD_1FTEST”.”DEPT1″.”NAME” (actual: 19, maximum: 10)   I While inserting into dept1 table 8
12899 ORA-12899: value too large for column “RFD_1FTEST”.”DEPT1″.”NAME” (actual: 19, maximum: 10)   I While inserting into dept1 table 9
12899 ORA-12899: value too large for column “RFD_1FTEST”.”DEPT1″.”NAME” (actual: 21, maximum: 10)   I While inserting into dept1 table 10

 

Explanation for above result set – The first 4 records Oracle error number 1722 – is due to insertion of character value in number field in EMP1 table.

The next 4 records Oracle error number 12899 is due to insertion of value larger than expected into DEPT1 table name field.

Note the ID column – that has the value of respective ID’s for the table concerned. Now, we can easily identify which table it belongs to from ORA_ERR_TAG$ table – which contains the description. In this way we can make DML error loging process generic and efficient. DML error logging is quite good when we try to perform INSERT based on SELECT from global temporary table / normal table. As there is no direct error logging feature for bulk inserts using SELECT as we have it for BULK DML operations using COLLECTIONS.

Having said all this - its quite important to know why it had caused the error in first place.

September 16, 2010  2:04 AM

Import Stats from one DB to another – Quick way

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

 

 

 

Database 1- 

exec

 

 

 

 

 

 

 

 dbms_stats.create_stat_table(ownname => <DBNMAME>, stattab => ‘STATS’);

exec dbms_stats.export_table_stats(<DBNAME>,‘<table name>’,null,’STATS’,null,true,null);

Database 2-

CREATE TABLE STATS AS SELECT * FROM STATS@<DBNAME>; 

 

exec

 

 

 

 

 

 

 

dbms_stats.import_table_stats <DBAME2>,‘<table name>’,null,‘STATS’,null,true,<DBNAME2>);


June 25, 2010  9:13 AM

Analytical way – To find Nth maximum value from a table

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

To find NTH maximum value from a table, we may need to query the same table twice to get the desired output. This will lead to table scans / index scans for the same table twice. The same can be achieved via analytical functions by querying the table once. Have enclosed the execution plan and have highlighted the difference.

Non analytical way

SQL> SELECT DISTINCT SAL FROM SCOTT.EMP A WHERE &N=(SELECT COUNT (DISTINCT B.SAL
)
2 FROM SCOTT.EMP B WHERE A.SAL<=B.SAL);
Enter value for n: 5
old 1: SELECT DISTINCT SAL FROM SCOTT.EMP A WHERE &N=(SELECT COUNT (DISTINCT B
.SAL)
new 1: SELECT DISTINCT SAL FROM SCOTT.EMP A WHERE 5=(SELECT COUNT (DISTINCT B.
SAL)

SAL
———-
2450

Execution Plan
———————————————————-
Plan hash value: 3556451907

—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 4 | 25 (4)| 00:00:01 |
| 1 | HASH UNIQUE | | 1 | 4 | 25 (4)| 00:00:01 |
|* 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL | EMP | 14 | 56 | 3 (0)| 00:00:01 |
| 4 | SORT GROUP BY | | 1 | 4 | | |
|* 5 | TABLE ACCESS FULL| EMP | 1 | 4 | 3 (0)| 00:00:01 |
—————————————————————————–

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

2 – filter( (SELECT COUNT(DISTINCT “B”.”SAL”) FROM “SCOTT”.”EMP” “B” WHERE “B”.”SAL”>=:B1)=5)
5 – filter(“B”.”SAL”>=:B1)

Statistics
———————————————————-
187 recursive calls
0 db block gets
127 consistent gets
4 physical reads
0 redo size
414 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
17 sorts (memory)
0 sorts (disk)
1 rows processed

USING Analytical functions -

SELECT * FROM
(SELECT e.*, RANK() OVER ( ORDER BY e.SAL DESC) rnk FROM scott.emp e) qry
WHERE qry.rnk = 6;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNK
———- ———-
7782 CLARK MANAGER 7839 09-JUN-81 2450
10 6

Execution Plan
———————————————————-
Plan hash value: 3291446077

——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
——————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 1400 | 4 (25)| 00:00:01|
|* 1 | VIEW | | 14 | 1400 | 4 (25)| 00:00:01|
|* 2 | WINDOW SORT PUSHED RANK| | 14 | 518 | 4 (25)| 00:00:01|
| 3 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

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

1 – filter(“QRY”.”RNK”=6)
2 – filter(RANK() OVER ( ORDER BY INTERNAL_FUNCTION(“E”.”SAL”) DESC)
SQL> SELECT * FROM
2 (SELECT e.*, RANK() OVER ( ORDER BY e.SAL DESC) rnk FROM scott.emp e) qry
3 WHERE qry.rnk = 6;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNK
———- ———-
7782 CLARK MANAGER 7839 09-JUN-81 2450
10 6

Execution Plan
———————————————————-
Plan hash value: 3291446077

——————————————————————————–

-

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|

——————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 1400 | 4 (25)| 00:00:01|
|* 1 | VIEW | | 14 | 1400 | 4 (25)| 00:00:01|
|* 2 | WINDOW SORT PUSHED RANK| | 14 | 518 | 4 (25)| 00:00:01|
| 3 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

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

1 – filter(“QRY”.”RNK”=6)
2 – filter(RANK() OVER ( ORDER BY INTERNAL_FUNCTION(“E”.”SAL”) DESC
)<=6)

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


June 25, 2010  9:12 AM

Analytical way – Retrieve the Nth row from a table

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

To retrieve Nth row from a table using the usual way it might require to query the same table thrice (can write it in different way also). But, with Analytical function the same can be made to hit only once. Using this kind of analytical functions is particularly useful when the query needs to be part of a huge query – helps to avoid group by – so the need to query the same table multiple times is reduced.

Non Analytical way

One way to do this – Usual – three table hits.

select sal FROM scott.emp WHERE rowid = (SELECT rowid FROM scott.emp WHERE rownum <= 5 MINUS SELECT rowid FROM scott.emp WHERE rownum < 5);

SAL
———-
1250

Execution Plan
———————————————————-
Plan hash value: 791824717

——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
——————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 16 | 5 (40)| 00:00:01 |
| 1 | TABLE ACCESS BY USER ROWID| EMP | 1 | 16 | 1 (0)| 00:00:01 |
| 2 | MINUS | | | | | |
| 3 | SORT UNIQUE | | 5 | 60 | 2 (50)| 00:00:01 |
|* 4 | COUNT STOPKEY | | | | | |
| 5 | INDEX FULL SCAN | PK_EMP | 14 | 168 | 1 (0)| 00:00:01 |
| 6 | SORT UNIQUE | | 4 | 48 | 2 (50)| 00:00:01 |
|* 7 | COUNT STOPKEY | | | | | |
| 8 | INDEX FULL SCAN | PK_EMP | 14 | 168 | 1 (0)| 00:00:01 |
——————————————————————————–

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

4 – filter(ROWNUM<=5)
7 – filter(ROWNUM<5)

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

Using Analytical functions

SQL> SELECT a.* FROM
(select e.*,row_number() OVER (order by rownum) rnm FROM scott.emp e) a
WHERE rnm = 5;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNM
———- ———-
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400
30 5

Execution Plan
———————————————————-
Plan hash value: 1602237660

——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
——————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 1400 | 4 (25)| 00:00:01|
|* 1 | VIEW | | 14 | 1400 | 4 (25)| 00:00:01|
|* 2 | WINDOW SORT PUSHED RANK| | 14 | 518 | 4 (25)| 00:00:01|
| 3 | COUNT | | | | ||
| 4 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

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

1 – filter(“RNM”=5)
2 – filter(ROW_NUMBER() OVER ( ORDER BY ROWNUM)<=5)

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


June 25, 2010  9:12 AM

Analytical way – Retrieve say 5 to 8 rows from table

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

Non-Analytical way

SELECT a.* FROM
(select e.*,ROWNUM rnm FROM scott.emp e) a
WHERE rnm BETWEEN 3 AND 8;

SQL> SELECT a.* FROM
2 (select e.*,ROWNUM rnm FROM scott.emp e) a
3 WHERE rnm BETWEEN 3 AND 8;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNM
———- ———-
7521 WARD SALESMAN 7698 22-FEB-81 1250 500
30 3

7566 JONES MANAGER 7839 02-APR-81 2975
20 4

7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400
30 5

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNM
———- ———-
7698 BLAKE MANAGER 7839 01-MAY-81 2850
30 6

7782 CLARK MANAGER 7839 09-JUN-81 2450
10 7

7788 SCOTT ANALYST 7566 19-APR-87 3000
20 8

6 rows selected.

Execution Plan
———————————————————-
Plan hash value: 2077119879

—————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————-
| 0 | SELECT STATEMENT | | 14 | 1400 | 3 (0)| 00:00:01 |
|* 1 | VIEW | | 14 | 1400 | 3 (0)| 00:00:01 |
| 2 | COUNT | | | | | |
| 3 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
—————————————————————————-

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

1 – filter(“RNM”=3)

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

SQL>

OR

SELECT * FROM scott.emp WHERE rowid in (SELECT rowid FROM scott.emp WHERE rownum <= 8
MINUS
SELECT rowid FROM scott.emp WHERE rownum < 3);

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
———- ———- ——— ———- ——— ———- ———- ———-
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20

6 rows selected.

Execution Plan
———————————————————-
Plan hash value: 986843382
——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
——————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 49 | 8 (38)| 00:00:01|
|* 1 | HASH JOIN | | 1 | 49 | 8 (38)| 00:00:01|
| 2 | VIEW | VW_NSO_1 | 8 | 96 | 4 (50)| 00:00:01|
| 3 | MINUS | | | | ||
| 4 | SORT UNIQUE | | 8 | 96 | ||
|* 5 | COUNT STOPKEY | | | | ||
| 6 | INDEX FULL SCAN| PK_EMP | 14 | 168 | 1 (0)| 00:00:01|
| 7 | SORT UNIQUE | | 2 | 24 | ||
|* 8 | COUNT STOPKEY | | | | ||
| 9 | INDEX FULL SCAN| PK_EMP | 14 | 168 | 1 (0)| 00:00:01|
| 10 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

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

1 – access(ROWID=”ROWID”)
5 – filter(ROWNUM<=8)
8 – filter(ROWNUM<3)

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

Using Analytical functions

SQL> SELECT a.* FROM
(select e.*,row_number() OVER (order by rownum) rnm FROM scott.emp e) a
WHERE rnm BETWEEN 3 AND 8;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNM
———- ———-
7521 WARD SALESMAN 7698 22-FEB-81 1250 500
30 3
7566 JONES MANAGER 7839 02-APR-81 2975
20 4
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400
30 5
7698 BLAKE MANAGER 7839 01-MAY-81 2850
30 6
7782 CLARK MANAGER 7839 09-JUN-81 2450
10 7
7788 SCOTT ANALYST 7566 19-APR-87 3000
20 8

6 rows selected.

Execution Plan
———————————————————-
Plan hash value: 1602237660

——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
——————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 1400 | 4 (25)| 00:00:01|
|* 1 | VIEW | | 14 | 1400 | 4 (25)| 00:00:01|
|* 2 | WINDOW SORT PUSHED RANK| | 14 | 518 | 4 (25)| 00:00:01|
| 3 | COUNT | | | | ||
| 4 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

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

1 – filter(“RNM”>=3 AND “RNM”<=8)
2 – filter(ROW_NUMBER() OVER ( ORDER BY ROWNUM)<=8)

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


June 25, 2010  9:11 AM

Analytical way – TOP N rows from a table

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

Non Analytical way

SELECT * FROM scott.emp inside WHERE 5 >= (SELECT COUNT (DISTINCT outside.sal) FROM scott.emp outside
WHERE outside.sal >= inside.sal) ORDER BY inside.sal DESC;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
———- ———- ——— ———- ——— ———- ———- ——-
7839 KING PRESIDENT 17-NOV-81 5000 10
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7566 JONES MANAGER 7839 02-APR-81 2975 20
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10

6 rows selected.

Execution Plan
———————————————————-
Plan hash value: 1721702769

—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 37 | 25 (4)| 00:00:01 |
| 1 | SORT ORDER BY | | 1 | 37 | 25 (4)| 00:00:01 |
|* 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 4 | SORT GROUP BY | | 1 | 4 | | |
|* 5 | TABLE ACCESS FULL| EMP | 1 | 4 | 3 (0)| 00:00:01 |
—————————————————————————–

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

2 – filter( (SELECT COUNT(DISTINCT “OUTSIDE”.”SAL”) FROM
“SCOTT”.”EMP” “OUTSIDE” WHERE “OUTSIDE”.”SAL”>=:B1)=:B1)

Statistics
———————————————————-
1 recursive calls
0 db block gets
91 consistent gets
0 physical reads
0 redo size
1028 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
13 sorts (memory)
0 sorts (disk)
6 rows processed

SQL>

Analytical way
SQL> SELECT a.* FROM
2 (select e.*,row_number() OVER (order by rownum) rnm FROM scott.emp e) a
3 WHERE rnm BETWEEN 1 AND 6;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO RNM
———- ———-
7369 SMITH CLERK 7902 17-DEC-80 800
20 1
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300
30 2
7521 WARD SALESMAN 7698 22-FEB-81 1250 500
30 3
7566 JONES MANAGER 7839 02-APR-81 2975
20 4
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400
30 5
7698 BLAKE MANAGER 7839 01-MAY-81 2850
30 6

6 rows selected.

Execution Plan
———————————————————-
Plan hash value: 1602237660

——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
——————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 1400 | 4 (25)| 00:00:01|
|* 1 | VIEW | | 14 | 1400 | 4 (25)| 00:00:01|
|* 2 | WINDOW SORT PUSHED RANK| | 14 | 518 | 4 (25)| 00:00:01|
| 3 | COUNT | | | | ||
| 4 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01|
——————————————————————————–

-

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

1 – filter(“RNM”>=1 AND “RNM”<=6)
2 – filter(ROW_NUMBER() OVER ( ORDER BY ROWNUM)<=6)

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

SQL>


June 25, 2010  9:10 AM

Analytical way – TO FIND RUNNING TOTAL

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

To find the running total using the non-analytical way minimum the same table needs to be joined twice. Using Analytical functions the same can be achieved with single table hit. In a single query running total can be computed in no time.

NON ANALYTICAL WAY -

Select emp1.empno, emp1.sal, sum(emp2.sal) from scott.emp emp1, scott.emp emp2
where emp2.empno <= emp1.empno group by emp1.empno, emp1.sal
order by emp1.empno

EMPNO SAL SUM(EMP2.SAL)
———- ———- ————-
7369 800 800
7499 1600 2400
7521 1250 3650
7566 2975 6625
7654 1250 7875
7698 2850 10725
7782 2450 13175
7788 3000 16175
7839 5000 21175
7844 1500 22675
7876 1100 23775
7900 950 24725
7902 3000 27725
7934 1300 29025

14 rows selected.

Execution Plan
———————————————————-
Plan hash value: 1016356015
——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
——————————————————————————–
| 0 | SELECT STATEMENT | | 10 | 160 | 7 (29)| 0
0:00:01 |
| 1 | SORT GROUP BY | | 10 | 160 | 7 (29)| 0
0:00:01 |
| 2 | MERGE JOIN | | 10 | 160 | 6 (17)| 0
0:00:01 |
| 3 | SORT JOIN | | 14 | 112 | 2 (0)| 0
0:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 112 | 2 (0)| 0
0:00:01 |
| 5 | INDEX FULL SCAN | PK_EMP | 14 | | 1 (0)| 0
0:00:01 |
|* 6 | SORT JOIN | | 14 | 112 | 4 (25)| 0
0:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 112 | 3 (0)| 0
0:00:01 |

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

6 – access(INTERNAL_FUNCTION(“EMP2″.”EMPNO”)<=INTERNAL_FUNCTION(“EMP1″.”EMPNO
” )) filter(INTERNAL_FUNCTION(“EMP2″.”EMPNO”)<=INTERNAL_FUNCTION(“EMP1″.”EMPNO
” ))

Statistics
———————————————————-
1 recursive calls
0 db block gets
9 consistent gets
0 physical reads
0 redo size
790 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
14 rows processed

SQL>

ANALYTICAL WAY

SQL> set autotrace on
SQL> Select e1.empno, e1.sal, sum(e1.sal) OVER (ORDER BY rownum) from scott.emp
e1 order by e1.empno;

EMPNO SAL SUM(E1.SAL)OVER(ORDERBYROWNUM)
———- ———- ——————————
7369 800 800
7499 1600 2400
7521 1250 3650
7566 2975 6625
7654 1250 7875
7698 2850 10725
7782 2450 13175
7788 3000 16175
7839 5000 21175
7844 1500 22675
7876 1100 23775
7900 950 24725
7902 3000 27725
7934 1300 29025

14 rows selected.

Execution Plan
———————————————————-
Plan hash value: 3990564813

—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 14 | 112 | 5 (40)| 00:00:01 |
| 1 | SORT ORDER BY | | 14 | 112 | 5 (40)| 00:00:01 |
| 2 | WINDOW SORT | | 14 | 112 | 5 (40)| 00:00:01 |
| 3 | COUNT | | | | | |
| 4 | TABLE ACCESS FULL| EMP | 14 | 112 | 3 (0)| 00:00:01 |
—————————————————————————–

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

SQL>


June 25, 2010  9:10 AM

Analytical way – TO FIND LAST N ROWS AND DISPLAY ROWNUMBER IN A TABLE

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

To find the last N and display the row number.

With the usual way to pick last set of records from any table minimum 3 joins are required. Using analytical functions the same can be reduced to single table hit to retrieve the last N rows and display the row numbers for the same. This is particularly useful when something like this needs to be achieved in a big query that has multiple joins and multiple column fetch.

NON ANALYTICAL WAY

select * from scott.emp minus select * from scott.emp where rownum select * from scott.emp minus select * from scott.emp where rownum <
2 (select count(*) – 10 from scott.emp);

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
———- ———- ——— ———- ——— ———- ———- ——-
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10

11 rows selected.

Execution Plan
———————————————————-
Plan hash value: 2564595189

——————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
——————————————————————————-
| 0 | SELECT STATEMENT | | 14 | 1036 | 9 (67)| 00:00:01 |
| 1 | MINUS | | | | | |
| 2 | SORT UNIQUE | | 14 | 518 | 4 (25)| 00:00:01 |
| 3 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 14 | 518 | 5 (20)| 00:00:01 |
|* 5 | COUNT STOPKEY | | | | | |
| 6 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | | | |
| 8 | INDEX FULL SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
——————————————————————————-

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

5 – filter( (SELECT COUNT(*)-10 FROM “SCOTT”.”EMP” “EMP”)>ROWNUM)

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

SQL>

ANALYTICAL WAY

SELECT qry.empno, qry.ename, qry.job, qry.mgr, qry.hiredate,qry.sal,qry.comm,qry.deptno,qry.row_desc
from
(SELECT A.*, COUNT(1) OVER (ORDER BY ROWNUM ASC) ROW_ASC, COUNT(1) OVER (ORDER BY ROWNUM DESC) ROW_DESC FROM SCOTT.EMP A) QRY where qry.row_Asc between 1 and 11

SQL> SELECT qry.empno, qry.ename, qry.job, qry.mgr, qry.hiredate,qry.sal,qry.com
m,qry.deptno,qry.row_desc
2 from (SELECT A.*, COUNT(1) OVER (ORDER BY ROWNUM ASC) ROW_ASC, COUNT(1) OVE
R (ORDER BY ROWNUM DESC) ROW_DESC FROM SCOTT.EMP A) QRY where qry.row_Asc betwee
n 1 and 11;

EMPNO ENAME JOB MGR HIREDATE SAL COMM
———- ———- ——— ———- ——— ———- ———-
DEPTNO ROW_DESC
———- ———-
7876 ADAMS CLERK 7788 23-MAY-87 1100
20 4
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0
30 5
7839 KING PRESIDENT 17-NOV-81 5000
10 6
7788 SCOTT ANALYST 7566 19-APR-87 3000
20 7
7782 CLARK MANAGER 7839 09-JUN-81 2450
10 8
7698 BLAKE MANAGER 7839 01-MAY-81 2850
30 9
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400
30 10
7566 JONES MANAGER 7839 02-APR-81 2975
20 11
7521 WARD SALESMAN 7698 22-FEB-81 1250 500
30 12
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300
30 13
7369 SMITH CLERK 7902 17-DEC-80 800
20 14

11 rows selected.

Execution Plan
———————————————————-
Plan hash value: 2175649969

——————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
——————————————————————————
| 0 | SELECT STATEMENT | | 14 | 1582 | 5 (40)| 00:00:01 |
|* 1 | VIEW | | 14 | 1582 | 5 (40)| 00:00:01 |
| 2 | WINDOW SORT | | 14 | 518 | 5 (40)| 00:00:01 |
| 3 | WINDOW SORT | | 14 | 518 | 5 (40)| 00:00:01 |
| 4 | COUNT | | | | | |
| 5 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
——————————————————————————

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

1 – filter(“QRY”.”ROW_ASC”>=1 AND “QRY”.”ROW_ASC”<=11)

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


June 25, 2010  6:42 AM

Migrating from 9i to 11g – Collections Enhancements

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

1 Indicies of

New feature in Oracle 10g.

In FORALL collections.FIRST .. collections.LAST it is not possible to use the indices sequntially if the collection is sparse. But, the same can be handled in Oracle 10g by using FORALL IN INDICES OF keyword.

create table emp1(empno number, still_employed varchar2(20))

insert into emp1 values (1, ‘Y’);
insert into emp1 values (2, ‘N’);
insert into emp1 values (3, ‘Y’);

Oracle 9i
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
TYPE emp_list IS TABLE OF emp1%ROWTYPE;
emp_id_tab emp_id_list ;
emp_tab emp_list := emp_list();
BEGIN

emp_Tab.extend;
emp_tab(1).empno := 10;
emp_Tab.extend;
emp_tab(2).empno := 100;
emp_Tab.extend;
emp_Tab.extend;
emp_tab(4).empno := 1000;

emp_Tab.extend;
emp_id_tab(1) := ‘Y’;
emp_Tab.extend;
emp_id_tab(2) := ‘N’;
emp_Tab.extend;
emp_Tab.extend;
emp_id_tab(4) := ‘Y’;

FORALL i IN emp_id_tab.first .. emp_id_tab.last
UPDATE EMP1 SET ROW = emp_tab(i)
WHERE still_employed = emp_id_tab(i);
END;
/

OUTPUT

DECLARE
*
ERROR at line 1:
ORA-22160: element at index [3] does not exist
ORA-06512: at line 24

Oracle 11g
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
TYPE emp_list IS TABLE OF emp1%ROWTYPE;
emp_id_tab emp_id_list ;
emp_tab emp_list := emp_list();
BEGIN

emp_Tab.extend;
emp_tab(1).empno := 10;
emp_Tab.extend;
emp_tab(2).empno := 100;
emp_Tab.extend;
emp_Tab.extend;
emp_tab(4).empno := 1000;

emp_Tab.extend;
emp_id_tab(1) := ‘Y’;
emp_Tab.extend;
emp_id_tab(2) := ‘N’;
emp_Tab.extend;
emp_Tab.extend;
emp_id_tab(4) := ‘Y’;

FORALL i IN INDICES OF emp_id_tab
UPDATE EMP1 SET ROW = emp_tab(i)
WHERE still_employed = emp_id_tab(i);
END;
/

OUTPUT

PL/SQL procedure successfully completed.

2 Values of

New feature in Oracle 10g.

VALUES OF clause enables to match the elements of one collection against the value of another collection and helps to perform DML operations based on the same.

DELETE FROM EMP1;

Oracle 9i
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
emp_id_tab emp_id_list;

TYPE emp_list IS TABLE OF emp1%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab emp_list;
BEGIN

emp_id_tab(1) := 10;
emp_id_tab(2) := 9;
emp_id_tab(3) := 8;

SELECT rownum,’Y’ BULK COLLECT INTO emp_tab
FROM emp
WHERE ROWNUM <= 50;

FORALL i IN emp_id_tab.FIRST .. emp_id_tab.LAST
INSERT INTO EMP1 VALUES emp_tab(i);
END;
/

OUTPUT

PL/SQL procedure successfully completed.

EMPNO STILL_EMPLOYED
1 Y
2 Y
3 Y

Only solution is – create nested table and perform and compare empno against TABLE(nested_table) in SELECT statement.

Oracle 11g
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
emp_id_tab emp_id_list;

TYPE emp_list IS TABLE OF emp1%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab emp_list;
BEGIN

emp_id_tab(1) := 10;
emp_id_tab(2) := 9;
emp_id_tab(3) := 8;

SELECT rownum,’Y’ BULK COLLECT INTO emp_tab
FROM emp
WHERE ROWNUM <= 50;

FORALL i IN VALUES OF emp_id_tab
INSERT INTO EMP1 VALUES emp_tab(i);
END;
/

OUTPUT

PL/SQL procedure successfully completed.

EMPNO STILL_EMPLOYED
10 Y
9 Y
8 Y

If we note the above results, the VALUES OF clause exactly matches the elements of one collection vs the elements of other collection and inserts values. This cannot be achieved in Oracle 9i unless we explicitly match up the elements in the WHERE clause.

3 Error handling

Oracle 9i
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
emp_id_tab emp_id_list;

TYPE emp_list IS TABLE OF emp1%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab emp_list;
BEGIN

emp_id_tab(1) := 10;
emp_id_tab(2) := 9;
emp_id_tab(3) := 100;

SELECT rownum,’Y’ BULK COLLECT INTO emp_tab
FROM emp
WHERE ROWNUM <= 50;

FORALL i IN emp_id_tab.FIRST .. emp_id_tab.LAST
INSERT INTO EMP1 VALUES emp_tab(i);

EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(‘Error message ‘ || SQLERRM);
END;
/

OUTPUT

PL/SQL procedure successfully completed.
EMPNO STILL_EMPLOYED
1 Y
2 Y
3 Y
This will still work – because it does not try to match the elements.

Oracle 11g
SET SERVEROUTPUT ON SIZE 10000
DECLARE
TYPE emp_id_list IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
emp_id_tab emp_id_list;

TYPE emp_list IS TABLE OF emp1%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab emp_list;
BEGIN

emp_id_tab(1) := 10;
emp_id_tab(2) := 9;
emp_id_tab(3) := 100;

SELECT rownum,’Y’ BULK COLLECT INTO emp_tab
FROM emp
WHERE ROWNUM <= 50;

FORALL i IN VALUES OF emp_id_tab
INSERT INTO EMP1 VALUES emp_tab(i);

EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(‘Error message ‘ || SQLERRM);

END;
/

OUTPUT

Error message ORA-22160: element at index [100] does not exist

4 Collect
New feature from Oracle 11g Release 1

COLLECT enables to transform rows into columns just based on a single function.

Running it on sql developer -

Oracle 11g
select deptno , collect(ename) enm from scott.emp
group by deptno ;

10 VARCHAR(CLARK,KING,MILLER)
20 VARCHAR(SMITH,FORD,ADAMS,SCOTT,JONES)
30 VARCHAR(ALLEN,BLAKE,MARTIN,TURNER,JAMES,WARD)

We cannot do something like this !!

SELECT qry.dno, SUBSTR(qry.enm,8) empnm from
(select deptno dno , collect(ename) enm from scott.emp
group by deptno ) qry

Running it on sqlplus with report related command-

SQL> break on deptno skip 1;

SQL> select deptno , collect(ename) as empnm from scott.emp group by deptno ;

Oracle 11g
DEPTNO EMPNM
——————————————————————————–
10 SYSTPi7LdWP3QSeisIuh7s78iIg==(‘CLARK’, ‘KING’, ‘MILLER’)
20 SYSTPi7LdWP3QSeisIuh7s78iIg==(‘SMITH’, ‘FORD’, ‘ADAMS’, ‘SCOTT’, ‘JONES’)
30 SYSTPi7LdWP3QSeisIuh7s78iIg==(‘ALLEN’, ‘BLAKE’, ‘MARTIN’, ‘TURNER’, ‘JAMES’, ‘WARD’)

5 Collection Assignment

Now, the collections assignment is improved with various additional features viz., multiset union, multiset intersect, multiset distcint etc.,
SET SERVEROUTPUT ON
DECLARE
TYPE software_tab IS TABLE OF VARCHAR2(1000);
soft_list_1 software_tab := software_tab(‘Oracle’,’C’,’C#’,’VB’,’Sql’);
soft_list_2 software_tab := software_tab(‘Oracle’,’C’,’PHP’,’Java’);
soft_list_3 software_tab;

BEGIN
– this is as usual
soft_list_3 := soft_list_1;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Assignment eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET UNION DISTINCT soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset union distinct eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET INTERSECT DISTINCT soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset intersect distinct eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET EXCEPT soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset except eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET INTERSECT soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset intersect eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET EXCEPT DISTINCT soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset except distinct eg ‘ || soft_list_3(i));
END LOOP;

soft_list_3 := soft_list_1 MULTISET UNION soft_list_2;
FOR i IN soft_list_3.first .. soft_list_3.last LOOP
DBMS_OUTPUT.put_line(‘Multiset union eg ‘ || soft_list_3(i));
END LOOP;
END;
/
OUTPUT-
Assignment eg Oracle
Assignment eg C
Assignment eg C#
Assignment eg VB
Assignment eg Sql
Multiset union distinct eg Oracle
Multiset union distinct eg C
Multiset union distinct eg C#
Multiset union distinct eg VB
Multiset union distinct eg Sql
Multiset union distinct eg PHP
Multiset union distinct eg Java
Multiset intersect distinct eg Oracle
Multiset intersect distinct eg C
Multiset except eg C#
Multiset except eg VB
Multiset except eg Sql
Multiset intersect eg Oracle
Multiset intersect eg C
Multiset except distinct eg C#
Multiset except distinct eg VB
Multiset except distinct eg Sql
Multiset union eg Oracle
Multiset union eg C
Multiset union eg C#
Multiset union eg VB
Multiset union eg Sql
Multiset union eg Oracle
Multiset union eg C
Multiset union eg PHP
Multiset union eg Java
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07

6 Improved comparisons

SET SERVEROUTPUT ON
DECLARE
TYPE software_tab IS TABLE OF VARCHAR2(10);
software_list_1 software_tab := software_tab(‘Oracle’,’C’,’C#’,’VB’,’Sql’);
software_list_2 software_tab := software_tab(‘Oracle’,’C’,’PHP’,’Java’);
software_list_3 software_tab;
BEGIN
IF (software_list_3 IS NULL) AND (software_list_1 IS NOT NULL) THEN
DBMS_OUTPUT.put_line(‘Value – list3 is null and list1 is not null’);
END IF;
software_list_3 := software_list_1;
IF (software_list_3 = software_list_1) AND (software_list_3 != software_list_2) THEN
DBMS_OUTPUT.put_line(‘list3 = list1 and list3 != list2 ‘);
END IF;

IF (SET(software_list_2) SUBMULTISET software_list_1) AND (software_list_1 NOT SUBMULTISET software_list_2) THEN
DBMS_OUTPUT.put_line( ‘list2 submultiset of list1 and list1 is not sub multiset of list2′);
END IF;

DBMS_OUTPUT.put_line(‘Duplicates related print list 2 -’ || CARDINALITY(software_list_2));

DBMS_OUTPUT.put_line( ‘Remove duplicates list2 – ‘ || CARDINALITY(SET(software_list_2)) || ‘ – Remove duplicates’);

IF software_list_2 IS NOT A SET THEN
DBMS_OUTPUT.put_line( ‘software_list_2 has duplicates’);
END IF;

IF software_list_3 IS NOT EMPTY THEN
DBMS_OUTPUT.put_line( ‘List3 is not empty’);
END IF;
END;
/
OUTPUT-
Value – list3 is null and list1 is not null
list3 = list1 and list3 != list2
Duplicates related print list 2 -4
Remove duplicates list2 – 4 – Remove duplicates
List3 is not empty
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01

7 Improved SET operator
Normal assignment assigns all the values including duplicates – SET assignment removes the duplicates before assignment.

SET SERVEROUTPUT ON
DECLARE
TYPE software_tab IS TABLE OF VARCHAR2(10);
software_list_1 software_tab := software_tab(‘Oracle’,’C’,’C#’,’VB’,’Sql’, ‘Oracle’,’Sql’);
software_list_2 software_tab;

BEGIN
software_list_2 := software_list_1;
FOR i IN software_list_2.first .. software_list_2.last LOOP
DBMS_OUTPUT.put_line(‘normal Assignment – ‘ || software_list_2(i));
END LOOP;

software_list_2 := SET(software_list_1);
FOR i IN software_list_2.first .. software_list_2.last LOOP
DBMS_OUTPUT.put_line(‘set assignment – ‘ || software_list_2(i));
END LOOP;

END;
/
OUTPUT-
normal Assignment – Oracle
normal Assignment – C
normal Assignment – C#
normal Assignment – VB
normal Assignment – Sql
normal Assignment – Oracle
normal Assignment – Sql
set assignment – Oracle
set assignment – C
set assignment – C#
set assignment – VB
set assignment – Sql
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.04

References : From internet.


June 25, 2010  6:19 AM

Migrating from 9i to 11g – Adaptive cursor sharing

Lakshmi Venkatesh Profile: Lakshmi Venkatesh

Feature available from : Oracle 11g Release 1

Cursor_Sharing enables to notice similar SQL statements that are already parsed and available in SQL area. When the query is issued for the first time, the same is stored in the SQL area. Later, on issuing same / similar SQL statements, the query in the memory would be processed – parsing will not take place again. Default cursor sharing parameter is EXACT. Only if the SQL statement is exactly similar the query will be used otherwise it will be parsed again.

Setting cursor_sharing to FORCE or SIMILAR enables similar statements to share the SQL.
FORCE – forces similar SQL statements to share the SQL area, detoriating the explain plans.
SIMILAR – similar SQL statements to share the SQL area, without detoriating the explain plans.
EXACT – only exact SQL statements share the SQL area. This is the default value.

Caution: Setting CURSOR_SHARING to FORCE or SIMILAR prevents any outlines generated with literals from being used if they were generated with CURSOR_SHARING set to EXACT.

All the above three modes will work similarly for bind variables and literals. This results in bind variable peaking problem – it will choose the same explain plan however the data is distributed without considering the percentage of data returned by the predicate.

Oracle 11g has introduced a new feature adaptive cursor sharing to to choose different plan for queries containing bind variables on skewed data .

1. Literals

create index emp_idx1 on emp (deptno)

exec dbms_stats.gather_index_stats(ownname => ‘SYSTEM’, indname => ‘emp_idx1′)

exec dbms_stats.gather_table_stats ( ownname => ‘SYSTEM’, tabname => ‘EMP’, method_opt => ‘for all indexed columns size skewonly’, cascade => TRUE );

Oracle 9i

SQL> Select * from emp where deptno = 630;

500 rows selected.

Execution Plan
———————————————————-
Plan hash value: 3085206398
—————————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————-
| 0 | SELECT STATEMENT | | 8108 | 356K | 118 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID | EMP | 8108 | 356K | 118 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | EMP_IDX1 | 8108 | | 17 (0)| 00:00:01 |
—————————————————————————————-

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

2 – access(“DEPTNO”=630)

Statistics
———————————————————-
1 recursive calls
0 db block gets
79 consistent gets
0 physical reads
0 redo size
31937 bytes sent via SQL*Net to client
779 bytes received via SQL*Net from client
35 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
500 rows processed

Select * from emp where deptno = 550

23929 rows selected.

Elapsed: 00:00:00.29

Execution Plan
———————————————————-
Plan hash value: 3085206398
—————————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————-
| 0 | SELECT STATEMENT | | 8108 | 356K| 118 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID | EMP | 8108 | 356K| 118 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | EMP_IDX1 | 8108 | | 17 (0)| 00:00:01 |

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

2 – access(“DEPTNO”=550)

Statistics
———————————————————-
1 recursive calls
0 db block gets
3517 consistent gets
0 physical reads
0 redo size
1564144 bytes sent via SQL*Net to client
17961 bytes received via SQL*Net from client
1597 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
23929 rows processed

Note: For both the cases INDEX scan was performed.

Oracle 11g

alter session set “_optimizer_adaptive_cursor_sharing”=true

SQL> Select * from emp where deptno = 630;

500 rows selected.

Execution Plan
———————————————————-
Plan hash value: 3085206398
——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————-
| 0 | SELECT STATEMENT | | 517 | 25333 | 9 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 517 | 25333 | 9 (0)|00:00:01 |
|* 2 | INDEX RANGE SCAN | EMP_IDX1 | 517 | | 2 (0)| 00:00:01|
—————————————————————————————-

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

2 – access(“DEPTNO”=630)

Statistics
———————————————————-
1 recursive calls
0 db block gets
79 consistent gets
0 physical reads
0 redo size
31937 bytes sent via SQL*Net to client
779 bytes received via SQL*Net from client
35 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
500 rows processed

alter session set “_optimizer_adaptive_cursor_sharing”=true

Select * from emp where deptno = 550

SQL> Select * from emp where deptno = 550;

23929 rows selected.

Execution Plan
———————————————————-
Plan hash value: 3956160932
————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
————————————————————————–
| 0 | SELECT STATEMENT | | 23977 | 1147K| 143 (1) | 00:00:02|
|* 1 | TABLE ACCESS FULL| EMP | 23977 | 1147K| 143 (1)| 00:00:02|
————————————————————————–

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

1 – filter(“DEPTNO”=550)

Statistics
———————————————————-
1 recursive calls
0 db block gets
2099 consistent gets
0 physical reads
0 redo size
1272097 bytes sent via SQL*Net to client
17961 bytes received via SQL*Net from client
1597 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
23929 rows processed

With adaptive cursor sharing for the first query retrieving 500 records index scan; second query where 23929 records were fetched – FTS was performed.

2. Bind Variable Peeking

There could be columns with heavily skewed data or otherwise. It should be the case that heavily skewed data should use a different execution plan as opposed to normal data.

If a query needs to be tuned that has bind variable peeking issue then, we can deactivate bind peeking. It uses bind-aware cursor sharing.
Adaptive cursor sharing is the solution for bind variable peeking problem. It only shares the plan only if bind variables are equal. If the bind variables are equal and falls within the range then, it uses the same plan. If the bind values are not equivalent then it creates a new plan.

Oracle 9i

variable l_value number
exec :l_value := 5

SQL> Select * from test_bind where a = :l_value;
Elapsed: 00:00:00.00

Execution Plan
———————————————————-
Plan hash value: 317434058

—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 11 | 33 | 1 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| A_IDX1 | 11 | 33 | 1 (0)| 00:00:01 |
—————————————————————————

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

1 – access(“A”=TO_NUMBER(:L_VALUE))
variable l_value number
exec :l_value := 501

Select * from test_bind where a = :l_value;

Execution Plan
———————————————————-
Plan hash value: 317434058

—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 11 | 33 | 1 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| A_IDX1 | 11 | 33 | 1 (0)| 00:00:01 |
—————————————————————————

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

1 – access(“A”=TO_NUMBER(:L_VALUE))

Notes: In the above example even though the SQL returned more than 40% of data it goes for RANGE SCAN – just reuses the same plan.

Oracle 11g
CREATE TABLE bind_test (
object_id NUMBER,
object_type varchar2(100),
CONSTRAINT bind_test_pk PRIMARY KEY (object_id));

CREATE INDEX bind_test_idx ON bind_test(object_type);

BEGIN
FOR cur IN (select dummy_seq.nextval id, object_type from all_objects) LOOP
INSERT INTO bind_test VALUES (cur.id, cur.object_type);
end loop;
COMMIT;
END;
/

EXEC DBMS_STATS.gather_table_stats(USER, ‘bind_test’, method_opt=>’for all indexed columns size skewonly’, cascade=>TRUE);

alter session set “_optimizer_adaptive_cursor_sharing”=true
ALTER SESSION SET “_optim_peek_user_binds”=FALSE;

set autotrace traceonly

VARIABLE l_obj_type VARCHAR2(100)
EXEC :l_obj_type := ‘EDITION’

SELECT COUNT(object_id) FROM bind_test where object_type = :l_obj_type

Execution Plan
———————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
———————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 9 | 9 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |
|* 2 | INDEX RANGE SCAN| BIND_TEST_IDX | 1781 | 16029 | 9 (0)| 00:00:01 |
———————————————————————————–

VARIABLE l_obj_type VARCHAR2(100)
EXEC :l_obj_type := ‘PACKAGE’

SELECT COUNT(object_id) FROM bind_test where object_type = :l_obj_type

Execution Plan
——————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
——————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 9 | 51 (2)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |
|* 2 | TABLE ACCESS FULL| BIND_TEST | 1781 | 16029 | 51 (2)| 00:00:01 |
——————————————————————————–


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: