27 October 2006

Oracle Flashback Table

You can "rollback" a table in a past time in Oracle.


Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as SYS


SQL> grant select on v_$database to hr;

Grant succeeded

SQL> con hr/hr;
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as hr


SQL> create table flassh(x number);

Table created


SQL> alter table flassh enable row movement;

Table altered

SQL> insert into flassh values(1);

1 row inserted


SQL> commit;

Commit complete

SQL> SELECT db.CURRENT_SCN FROM v$database db;

CURRENT_SCN
-----------
    786903
4

SQL> insert into flassh values(1111);

1 row inserted

SQL> commit;

Commit complete


SQL> flashback table flassh to scn 7869034;

Done

SQL> SELECT * FROM flassh;

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


SQL> insert into flassh values(1111);

1 row inserted

SQL> commit;

Commit complete


SQL> SELECT * FROM flassh;

         X
----------
         1
      1111


SQL> update flassh set x = 5 where x = 1;

1 row updated

SQL> commit;

Commit complete

SQL> SELECT * FROM flassh;

         X
----------
         5
      1111


SQL> flashback table flassh to scn 7869034;

Done


SQL> SELECT * FROM flassh;

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


SQL>

22 October 2006

Bulk Processing Performance Analysis On Oracle

Analyzing performance statistics is very common procedure for oracle programmers. You can always do the same work with various ways. At this point you have to choose the most suitable one for your business. Especally this suitability stands on performance. Your boss wants to you for making prosceses faster ans faster. So, you have to analyze some performance statistics when doing some jobs.
If you are a good oracle programmer, you will always make your code "faster". The boss will pay more for this velocity. If you are always do your work "faster" you will never loss your job :)
Bulk processing comes in action when you are doing insert or "select" heavily. For instance if you are inserting 10000 row in pl/sql, there will be a context switching between two engines, PL/SQL and SQL engines. A context switch has a time payload. For this reason, to get more performant programs, you have to consider this.
For bulk processing you have to work with collections.
Below i demonstrated a simple example to show performance effects of bulk and nonbulk process. The demonstration was repeated with 50000, 100000 and 250000 rows.
The result is


Bulk Insert
Insert
%
Bulk Select
Select
%
50000
0,321
4,026
1154,21
0,11
0,17
54,55
100000
0,821
7,521
816,08
0,191
0,27
41,36
250000
0,941
21,891
2226,35
0,431
0,651
51,04



Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> create type number_list is table of number;
  2  /

Type created
Executed in 0,251 seconds
SQL> create table numbers(i number );
Table created
Executed in 0,091 seconds
SQL> BEGIN
  2    FOR i IN 1 .. 50000 LOOP
  3      INSERT INTO numbers values(i);
  4    END LOOP;
  5  END;
  6  /

PL/SQL procedure successfully completed
Executed in 4,026 seconds
SQL> commit;
Commit complete
Executed in 0,24 seconds
SQL> DECLARE
  2    total NUMBER := 0;
  3  BEGIN
  4    FOR rec IN (SELECT i FROM numbers) LOOP
  5      total := total + rec.i;
  6    END LOOP;
  7    dbms_output.put_line('Total is ' || total);
  8  END;
  9  /

Total is 1250025000
PL/SQL procedure successfully completed
Executed in 0,17 seconds
SQL> drop table numbers;
Table dropped
Executed in 0,04 seconds
SQL> create table numbers(i number);
Table created
Executed in 0,05 seconds
SQL> DECLARE
  2    number_arr number_list;
  3  BEGIN
  4    number_arr := number_list();
  5    number_arr.EXTEND(50000);
  6    FOR i IN 1 .. 50000 LOOP
  7      number_arr(i) := i;
  8    END LOOP;
  9    FORALL i IN number_arr.FIRST .. number_arr.LAST
 10      INSERT INTO numbers VALUES (number_arr(i));
 11 
 12  END;
 13  /

PL/SQL procedure successfully completed
Executed in 0,321 seconds
SQL> commit;
Commit complete
Executed in 0 seconds
SQL> DECLARE
  2    total      NUMBER := 0;
  3    number_arr number_list;
  4  BEGIN
  5    number_arr := number_list();
  6    SELECT i BULK COLLECT INTO number_arr FROM numbers;
  7    FOR i IN number_arr.FIRST .. number_arr.LAST LOOP
  8      total := total + number_arr(i);
  9    END LOOP;
 10    dbms_output.put_line('Total is ' || total);
 11  END;
 12  /

Total is 1250025000
PL/SQL procedure successfully completed
Executed in 0,11 seconds

20 October 2006

Paging Techniques In Oracle

Oracle, mysql gibi bir limit cümlesi sunmadığı için, bunun bazı yöntemler ile yapılması gerekir. En temelde paging işleminde tutarsızlık olacağı unutulmamalıdır. Sorguyu ilk çektiğiniz anda commit olmayan bir kayıt, ikinci sayfada commit edilirse, bu yeni kaydı göremeyebilirsiniz. Paging için birden fazla yöntem olabilir. Ben bunlardan rownum ve row_number ile ilgili olanlarını belirtmeye çalışacağım. Daha fazla sözü uzatmadan örneklere geçmek istiyorum:

İlk Yöntem row_number ile:

 

SQL> create table pagination_test( x varchar2(128) );

Table created

Executed in 0,09 seconds


SQL> BEGIN
  2    FOR i IN 1 .. 1000000 LOOP
  3      INSERT INTO pagination_test VALUES (  'aassddff' || i);
  4    END LOOP;
  5    COMMIT;
  6  END;
  7  /

PL/SQL procedure successfully completed

Executed in 64,693 seconds


SQL> SELECT inner_view.x
  2    FROM (SELECT x, row_number() over( ORDER BY x ASC) row_num
  3            FROM pagination_test) inner_view
  4   WHERE inner_view.row_num BETWEEN 400500 AND 400510;

X
--------------------------------------------------------------------------------
aassddff460446
aassddff460447
aassddff460448
aassddff460449
aassddff46045
aassddff460450
aassddff460451
aassddff460452
aassddff460453
aassddff460454
aassddff460455

11 rows selected


Executed in 14,591 seconds

SQL>



İkinci Yöntem rownum ile:


SQL> drop table pagination_test;

Table dropped

Executed in 1,422 seconds


SQL> create table pagination_test( x varchar2(128) );

Table created

Executed in 0,02 seconds


SQL> BEGIN
  2    FOR i IN 1 .. 1000000 LOOP
  3      INSERT INTO pagination_test VALUES (  'aassddff' || i);
  4    END LOOP;
  5    COMMIT;
  6  END;
  7  /

PL/SQL procedure successfully completed

Executed in 74,487 seconds


SQL> SELECT outer_view.x
  2    FROM (SELECT inner_view.x, rownum inner_rownum
  3            FROM (SELECT x FROM pagination_test ORDER BY x ASC) inner_view
  4           WHERE rownum <= 400510) outer_view
  5   WHERE outer_view.inner_rownum >= 400500;

X
--------------------------------------------------------------------------------
aassddff460446
aassddff460447
aassddff460448
aassddff460449
aassddff46045
aassddff460450
aassddff460451
aassddff460452
aassddff460453
aassddff460454
aassddff460455

11 rows selected


Executed in 7,631 seconds

SQL>


Yukarıdaki sonuçlardan görüleceği üzere rownum ile içiçe 3 select ile yapılan sorgu daha hızlı çalışmıştır. Detaylar için her bir sorgunun execution plan ine bakılabilir

19 October 2006

LEFT OUTER JOIN and NULL Values

I have mentioned some join operations on my previous post. LEFT OUTER JOIN takes all records of "left" table; so some of records of "right" table can stay NULL. Suppose you need to make some calculations on that NULL fields. Equality operations can not work on NULL values. NULL values are special values. If you add a condition on that field like = or <= it will skip all NULL values which are taken by OUTER JOIN. For clarity follow the example:

Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> drop table eq_h_test;

Table dropped

SQL> create table eq_h_test( i number, z number );

Table created

SQL> INSERT INTO eq_h_test VALUES(1, 100 );

1 row inserted

SQL> INSERT INTO eq_h_test VALUES(1, 103 );

1 row inserted

SQL> INSERT INTO eq_h_test VALUES(1, 109 );

1 row inserted

SQL> INSERT INTO eq_h_test VALUES(2, 10 );

1 row inserted

SQL> INSERT INTO eq_h_test VALUES(2, 1020 );

1 row inserted

SQL> INSERT INTO eq_h_test VALUES(3, 1700 );

1 row inserted

SQL> drop table eq_test;

Table dropped

SQL> create table eq_test( i number, k varchar2(3) );

Table created

SQL> INSERT INTO eq_test VALUES( 1, 'aaa' );

1 row inserted

SQL> INSERT INTO eq_test VALUES( 2, 'fff' );

1 row inserted

SQL> INSERT INTO eq_test VALUES( 3, 'rrr' );

1 row inserted

SQL> INSERT INTO eq_test VALUES( 4, 'ttt' );

1 row inserted

SQL> INSERT INTO eq_test VALUES( 5, 'qqq' );

1 row inserted

SQL> commit;

Commit complete


Let's selecting and operating. Aggreagating functions return NULL values even if they have no record.(For more please read Aggregation Functions and NO_DATA_FOUND Exception In Oracle) I use the join field in where clause for equality. I can not get NULL values and can not get power of OUTER JOIN. So i use an extra clause that contains special equality for NULL values.
SQL> SELECT * FROM eq_test t LEFT OUTER JOIN eq_h_test h ON t.i = h.i;
         I K            I          Z
---------- --- ---------- ----------
         1 aaa          1        100
         1 aaa          1        103
         1 aaa          1        109
         2 fff          2         10
         2 fff          2       1020
         3 rrr          3       1700
         5 qqq           
         4 ttt           

8 rows selected
SQL> SELECT *
  2    FROM eq_test t
  3    LEFT OUTER JOIN eq_h_test h ON t.i = h.i
  4   WHERE h.z = (SELECT MAX(h2.z) FROM eq_h_test h2 WHERE h2.i = h.i);

         I K            I          Z
---------- --- ---------- ----------
         1 aaa          1        109
         2 fff          2       1020
         3 rrr          3       1700

SQL> SELECT *
  2    FROM eq_test t
  3    LEFT OUTER JOIN eq_h_test h ON t.i = h.i
  4   WHERE decode(h.z,
  5                (SELECT MAX(h2.z) FROM eq_h_test h2 WHERE h2.i = h.i),
  6                1,
  7                0) = 1;

         I K            I          Z
---------- --- ---------- ----------
         1 aaa          1        109
         2 fff          2       1020
         3 rrr          3       1700
         5 qqq           
         4 ttt           

SQL>  SELECT *
  2     FROM eq_test t
  3     LEFT OUTER JOIN eq_h_test h ON t.i = h.i
  4    WHERE h.z = (SELECT MAX(h2.z) FROM eq_h_test h2 WHERE h2.i = h.i)
  5       OR h.z IS NULL;

         I K            I          Z
---------- --- ---------- ----------
         1 aaa          1        109
         2 fff          2       1020
         3 rrr          3       1700
         5 qqq           
         4 ttt           

SQL> ---
You must keep in mind NULL values are special. You must handle them. An alternative way, you can use DECODE for equality of NULLs. I wrote a simple example to show this. You can also get more information in my On NULL Values   post.
SQL>
SQL> SELECT decode(NULL, NULL, 'null is null', 'null is not null') FROM dual;

DECODE(NULL,NULL,'NULLISNULL',
------------------------------
null is null

SQL> --
SQL> SELECT dummy FROM dual WHERE 1 = 1;

DUMMY
-----
X

SQL> SELECT dummy FROM dual WHERE NULL = NULL;
DUMMY
-----

SQL> SELECT dummy FROM dual WHERE NULL IS NULL;
DUMMY
-----
X

SQL>

How To Join Tables In Oracle With LEFT RIGHT OUTER INNER FULL Keywords

In relational database systems, information is stored in multiple tables. You joins multiple tables to get information. Standart SQL, has defined join to combine multiple set based on desired attributes.
I simply demonstrate a complete example to show how to joins take place in Oracle.


Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> drop table student;

Table dropped

SQL> drop table project;

Table dropped

SQL> drop table course;

Table dropped

SQL> create table student(student_id number, taken_project_id number, given_course_id number, last_name varchar2(32) );

Table created

SQL> create table project(project_id number, project_description varchar2(32));

Table created

SQL> create table course(course_id number, course_description varchar2(32));

Table created

SQL> INSERT INTO course values(1, 'Database Management Sys');

1 row inserted

SQL> INSERT INTO course values(2, 'Logic');

1 row inserted

SQL> INSERT INTO course values(3, 'Circuit Theory');

1 row inserted

SQL> INSERT INTO project values(100, 'Reservation System');

1 row inserted

SQL> INSERT INTO project values(101, 'Online Booking');

1 row inserted

SQL> INSERT INTO project values(102, 'Face Recog. with brute force');

1 row inserted

SQL> INSERT INTO project values(103, 'On Fly Form Generator');

1 row inserted

SQL> INSERT INTO student values(60001, null, null, 'Alonso');

1 row inserted

SQL> INSERT INTO student values(60002, 102, 1, 'Richar');

1 row inserted

SQL> INSERT INTO student values(60001, 100, 2, 'Tekbir');

1 row inserted

SQL> INSERT INTO student values(60001, null, 1, 'Sergio');

1 row inserted

SQL> INSERT INTO student values(60001, 101, null, 'Alexov');

1 row inserted

SQL> commit;

Commit complete

SQL> SELECT * FROM student;
STUDENT_ID TAKEN_PROJECT_ID GIVEN_COURSE_ID LAST_NAME
---------- ---------------- --------------- --------------------------------
     60001                                  Alonso
     60002              102               1 Richar
     60001              100               2 Tekbir
     60001                                1 Sergio
     60001              101                 Alexov

SQL> SELECT * FROM project;
PROJECT_ID PROJECT_DESCRIPTION
---------- --------------------------------
       100 Reservation System
       101 Online Booking
       102 Face Recog. with brute force
       103 On Fly Form Generator

SQL> SELECT * FROM course;
 COURSE_ID COURSE_DESCRIPTION
---------- --------------------------------
         1 Database Management Sys
         2 Logic
         3 Circuit Theory


SQL> SELECT st.last_name, pr.project_description FROM student st, project pr WHERE st.taken_project_id = pr.project_id;

LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Richar                           Face Recog. with brute force
Tekbir                           Reservation System
Alexov                           Online Booking

SQL> SELECT st.last_name, pr.project_description FROM student st INNER JOIN project pr ON st.taken_project_id = pr.project_id;
LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Richar                           Face Recog. with brute force
Tekbir                           Reservation System
Alexov                           Online Booking


SQL> SELECT st.last_name, pr.project_description FROM student st, project pr WHERE st.taken_project_id = pr.project_id(+);

LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Tekbir                           Reservation System
Alexov                           Online Booking
Richar                           Face Recog. with brute force
Sergio                          
Alonso                          

SQL> SELECT st.last_name, pr.project_description FROM student st LEFT OUTER JOIN project pr ON st.taken_project_id = pr.project_id;
LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Tekbir                           Reservation System
Alexov                           Online Booking
Richar                           Face Recog. with brute force
Sergio                          
Alonso                          


SQL> SELECT st.last_name, pr.project_description FROM student st, project pr WHERE st.taken_project_id(+) = pr.project_id;

LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Richar                           Face Recog. with brute force
Tekbir                           Reservation System
Alexov                           Online Booking
                                 On Fly Form Generator

SQL> SELECT st.last_name, pr.project_description FROM student st RIGHT OUTER JOIN project pr ON st.taken_project_id = pr.project_id;
LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Richar                           Face Recog. with brute force
Tekbir                           Reservation System
Alexov                           Online Booking
                                 On Fly Form Generator


SQL> --SELECT st.last_name, pr.project_description FROM student st, project pr WHERE st.taken_project_id(+) = pr.project_id(+);--ERROR
SQL> SELECT st.last_name, pr.project_description FROM student st FULL OUTER JOIN project pr ON st.taken_project_id = pr.project_id;

LAST_NAME                        PROJECT_DESCRIPTION
-------------------------------- --------------------------------
Tekbir                           Reservation System
Alexov                           Online Booking
Richar                           Face Recog. with brute force
Sergio                          
Alonso                          
                                 On Fly Form Generator

6 rows selected

SQL> SELECT st.last_name, pr.project_description, co.course_description FROM student st, project pr, course co WHERE st.taken_project_id = pr.project_id AND st.given_course_id = co.course_id;

LAST_NAME                        PROJECT_DESCRIPTION              COURSE_DESCRIPTION
-------------------------------- -------------------------------- --------------------------------
Richar                           Face Recog. with brute force     Database Management Sys
Tekbir                           Reservation System               Logic

SQL> SELECT st.last_name, pr.project_description, co.course_description FROM student st INNER JOIN project pr ON st.taken_project_id = pr.project_id INNER JOIN course co ON  st.given_course_id = co.course_id;
LAST_NAME                        PROJECT_DESCRIPTION              COURSE_DESCRIPTION
-------------------------------- -------------------------------- --------------------------------
Tekbir                           Reservation System               Logic
Richar                           Face Recog. with brute force     Database Management Sys


SQL> SELECT st.last_name, pr.project_description, co.course_description FROM student st FULL OUTER JOIN project pr ON st.taken_project_id = pr.project_id FULL OUTER JOIN course co ON  st.given_course_id = co.course_id;

LAST_NAME                        PROJECT_DESCRIPTION              COURSE_DESCRIPTION
-------------------------------- -------------------------------- --------------------------------
Sergio                                                            Database Management Sys
Richar                           Face Recog. with brute force     Database Management Sys
Tekbir                           Reservation System               Logic
                                 On Fly Form Generator           
Alonso                                                           
Alexov                           Online Booking                  
                                                                  Circuit Theory

7 rows selected
SQL>

18 October 2006

Using Sequences For Data Integrity

You can use as some keys in your tables. Especially these keys are unique and identifies the row of a table. Sometimes you take care of these keys. Incrementing the key values 1 by 1 is a useful practise. But how the incrementation must be?
There are some ways. One is creating a trigger and taking max value + 1 of table for new key value. This is not a good solution. Because of invalidates your data integrity. For multi user environments same values can be inserted to table. If you create a unique constraint on key column you can somehow handle duplicate values. In performance situation, it is not preferable.
To demonstrate this, i opened two sessions. i wrote a before trigger and insert 10000 records to table. As you see it will create duplicates.

Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> drop table t;

Table dropped
Executed in 0,121 seconds
SQL> create table t(x number, y number);
Table created
Executed in 0,02 seconds
SQL> INSERT INTO t(x, y) values(1,1);
1 row inserted
Executed in 0 seconds
SQL> CREATE OR REPLACE TRIGGER trg_t
  2    BEFORE INSERT ON t
  3    FOR EACH ROW
  4  DECLARE
  5    i t.x%TYPE;
  6  BEGIN
  7    SELECT MAX(x) INTO i FROM t;
  8    :NEW.x := i + 1;
  9  END trg_t;
 10  /

Trigger created
Executed in 0,02 seconds
SQL> INSERT INTO t(y) values(1);
1 row inserted
Executed in 0,01 seconds
SQL> SELECT * FROM t;
         X          Y
---------- ----------
         1          1
         2          1

Executed in 0,02 seconds
SQL> commit;
Commit complete
Executed in 0 seconds


Now i opened two sessions concurently and runs the same query both sides.

SQL>
SQL> BEGIN
  2    FOR i IN 3 .. 10000 LOOP
  3      INSERT INTO t( y) values( i);
  4      COMMIT;
  5    END LOOP;
  6  END
  7  ;
  8  /

PL/SQL procedure successfully completed
Executed in 60,046 seconds


--This is other session
PL/SQL procedure successfully completed
Executed in 68,199 seconds

Now checking the duplicates:
SQL> SELECT count(count(*)) FROM t group by x having count(*) > 1;
COUNT(COUNT(*))
---------------
            470

Executed in 0,15 seconds


As you see there are some duplicates. You can avoid by defining unique constraint. But again this will make performance worse.


On the other side you can create a sequence and every insert you can take nextval from sequence. It consists your data integrity gains performance.
SQL> drop table t;
Table dropped
Executed in 0,07 seconds
SQL> create table t(x number, y number);
Table created
Executed in 0,01 seconds
SQL> drop sequence seq_t;
Sequence dropped
Executed in 0,33 seconds
SQL> create sequence seq_t start with 1 increment by 1;
Sequence created
Executed in 0,01 seconds


Now i opened two sessions concurently and runs the same query both sides.
SQL>
SQL> BEGIN
  2    FOR i IN 3 .. 10000 LOOP
  3      INSERT INTO t(x, y) values(seq_t.nextval, i);
  4      COMMIT;
  5    END LOOP;
  6  END;
  7 
  8  /

PL/SQL procedure successfully completed
Executed in 4,627 seconds
PL/SQL procedure successfully completed
Executed in 5,077 seconds

Now checking the duplicates:
SQL> SELECT count(count(*)) FROM t group by x having count(*) > 1;
COUNT(COUNT(*))
---------------
              0

Executed in 0,15 seconds



When using key values it is better to use sequence. It is atomic.

16 October 2006

Passing Object Parameters To Functions Remotely In Oracle

Sometimes you need to invoke some functions in remote database via database link. Passing pirimitive types like number or varchar2 are not so dificult. But ow about passing user defined data types? When you encourage this, you may get consider some points.
Firstly, Oracle has some restrictions on object types when passing them remotely. You are not allowed to pass objects remote database's functions via db links. Although you create the same object both two databases, you can not pass parameter.

SQL>con db03/db03
SQL> DROP TYPE test_type;

Type dropped
SQL> CREATE OR REPLACE TYPE test_type IS OBJECT
  2  (
  3    i NUMBER,
  4    z VARCHAR2(12)
  5  )
  6  ;
  7  /

Type created
SQL> CREATE OR REPLACE PROCEDURE set_test_type(  pit_test_type   in    test_type, res out number)
  2  IS
  3  BEGIN
  4    res :=  pit_test_type.i;
  5  END;
  6  /

Procedure created
SQL>
SQL> con db01/db01
SQL> DROP TYPE test_type;

Type dropped
SQL> CREATE OR REPLACE TYPE test_type IS OBJECT
  2  (
  3    i NUMBER,
  4    z VARCHAR2(12)
  5  )
  6  ;
  7  /

Type created
SQL> drop public database link test_link;
Database link dropped
SQL> create public database link test_link
  2    connect to sysadm identified by sysadm
  3    using 'db03';

Database link created

SQL> DECLARE
  2    i NUMBER;
  3    t test_type := test_type(101, 'hello');
  4 
  5  BEGIN
  6   
set_test_type@test_link(t, i);
  7    DBMS_OUTPUT.PUT_LINE('i is ' || i);
  8  END;
  9  /

DECLARE
  i NUMBER;
  t test_type := test_type(101, 'hello');

BEGIN
 
set_test_type@test_link(t, i);
  DBMS_OUTPUT.PUT_LINE('i is ' || i);
END;

ORA-06550: line 6, column 3:
PLS-00306: wrong number or types of arguments in call to 'SET_TEST_TYPE'
ORA-06550: line 6, column 3:
PL/SQL: Statement ignored




You can create your type only locally. It is also not a solution. Oracle does not know your objects:

SQL>
SQL>con db01/db01
SQL> CREATE OR REPLACE PACKAGE set_test_type_pkg IS
  2    TYPE test_type IS RECORD(
  3      i NUMBER,
  4      z VARCHAR2(12));
  5    PROCEDURE set_test_type(pit_test_type IN test_type, res OUT NUMBER);
  6  END set_test_type_pkg;
  7  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY set_test_type_pkg IS
  2 
  3    PROCEDURE set_test_type(pit_test_type IN test_type, res OUT NUMBER) IS
  4    BEGIN
  5      res := pit_test_type.i;
  6    END;
  7  END set_test_type_pkg;
  8  /

Package body created
SQL> show err;
No errors for PACKAGE BODY SYSADM.SET_TEST_TYPE_PKG

SQL>con db03/db03
SQL> DECLARE
  2    i NUMBER;
  3    TYPE test_type IS RECORD(
  4      i NUMBER,
  5      z VARCHAR2(12));
  6    tt_test_type test_type;
  7  BEGIN
  8    tt_test_type.i := 101;
  9    tt_test_type.z := 'hiii';
 10   
set_test_type_pkg.set_test_type@test_link(tt_test_type, i);
 11    DBMS_OUTPUT.PUT_LINE('i is ' || i);
 12  END;
 13  /

DECLARE
  i NUMBER;
  TYPE test_type IS RECORD(
    i NUMBER,
    z VARCHAR2(12));
  tt_test_type test_type;
BEGIN
  tt_test_type.i := 101;
  tt_test_type.z := 'hiii';
 
set_test_type_pkg.set_test_type@test_link(tt_test_type, i);
  DBMS_OUTPUT.PUT_LINE('i is ' || i);
END;

ORA-06550: line 10, column 3:
PLS-00306: wrong number or types of arguments in call to 'SET_TEST_TYPE'
ORA-06550: line 10, column 3:
PL/SQL: Statement ignored

SQL>


You can overcome this situation by referencing a local type both the databases. Important thing is, you have to reference the same type in one database.
SQL>con db01/db01
SQL> DECLARE
  2    i NUMBER;
  3 
  4    tt_test_type
set_test_type_pkg.test_type@test_link;
  5  BEGIN
  6    tt_test_type.i := 101;
  7    tt_test_type.z := 'hiii';
  8   
set_test_type_pkg.set_test_type@test_link(tt_test_type, i);
  9    DBMS_OUTPUT.PUT_LINE('i is ' || i);
 10  END;
 11  /

i is 101
PL/SQL procedure successfully completed
SQL>

Naming and Coding Standards for Oracle

I have read a very useful article from William Robertson. You can read entire article from

11 October 2006

NVL == COALESCE == DECODE

Sometimes you can use many functions to achieve one goal. Today i saw a question on forums.oracle and i put it seperate here.
Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> drop table t;

Table dropped
SQL> create table t( a varchar2(1), b varchar2(1),c varchar2(1));
Table created
SQL>  INSERT INTO t VALUES ('a','b','c');
1 row inserted
SQL>   INSERT INTO t VALUES (null,'d','e');
1 row inserted
SQL>    INSERT INTO t VALUES (null, null, 'f');
1 row inserted
SQL>    INSERT INTO t VALUES (null, 'g', null);
1 row inserted
SQL>       INSERT INTO t VALUES ('h', null, 'i');
1 row inserted
SQL> SELECT * FROM t;
A B C
- - -
a b c
  d e
    f
  g
h   i

SQL> SELECT coalesce(a, b, c) FROM t;
COALESCE(A,B,C)
---------------
a
d
f
g
h

SQL> SELECT nvl(a, nvl(b, c)) FROM t;
NVL(A,NVL(B,C))
---------------
a
d
f
g
h

SQL> SELECT decode(a, NULL, decode(b, NULL, c, b), a) FROM t;
DECODE(A,NULL,DECODE(B,NULL,C,
------------------------------
a
d
f
g
h

SQL>

06 October 2006

Dependent Objects and Object Statuses In Oracle

A question was asked on package dependencies at oracleturk. I want to describe a bit more here:
Objects may have some dependencies on theirselves. When creating a package or a trigger you may need other objects. When compiling an object or executing an alter command, you make dependent objects invalid. On calling objects firstly Oracle check object status. If object s valid tehere is no problem. But if an object is invalid, Oracle tries to compile the dependent objects. If there is no problem, statement executed and object status will become valid.
Oracle finds the object dependents with some internal tables. You can find the dependencies via selecting user_dependencies view or executing dbms_utility.get_dependency procedure.
There is a demonstration below. p3 dependent of p2 and p2 dependent of p1.
Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> CREATE OR REPLACE PACKAGE p1 IS
  2 
  3    FUNCTION get_date RETURN DATE;
  4 
  5  END p1;
  6  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p1 IS
  2 
  3    FUNCTION get_date RETURN DATE IS
  4    BEGIN
  5      RETURN SYSDATE;
  6    END get_date;
  7 
  8  END p1;
  9  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P1

SQL> CREATE OR REPLACE PACKAGE p2 IS
  2 
  3    FUNCTION get_increment_date RETURN date DETERMINISTIC;
  4 
  5  END p2;
  6  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p2 IS
  2 
  3    FUNCTION get_increment_date RETURN date DETERMINISTIC IS
  4    BEGIN
  5      RETURN p1.get_date + 1;
  6    END get_increment_date;
  7 
  8  END p2;
  9  /

Package body created
SQL> show err;No errors for PACKAGE BODY HR.P2
SQL> CREATE OR REPLACE PACKAGE p3 IS
  2 
  3    PROCEDURE write_date;
  4 
  5 
  6  END p3;
  7  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p3 IS
  2 
  3    PROCEDURE write_date IS
  4    BEGIN
  5      dbms_output.put_line(p2.get_increment_date);
  6    END write_date;
  7 
  8  END p3;
  9  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P3

SQL> SELECT o.object_name, o.object_type, o.status FROM user_objects o WHERE o.object_name in ('P1','P2', 'P3');
OBJECT_NAME                                                                      OBJECT_TYPE         STATUS
-------------------------------------------------------------------------------- ------------------- -------
P1                                                                               PACKAGE             VALID
P1                                                                               PACKAGE BODY        VALID
P2                                                                               PACKAGE             VALID
P2                                                                               PACKAGE BODY        VALID
P3                                                                               PACKAGE             VALID
P3                                                                               PACKAGE BODY        VALID

6 rows selected
sds SQL> alter package p1 compile;
Package altered
SQL> SELECT ud.name, ud.type, ud.referenced_name FROM user_dependencies ud WHERE ud.referenced_name = 'P1';
NAME                           TYPE              REFERENCED_NAME
------------------------------ ----------------- ----------------------------------------------------------------
P1                             PACKAGE BODY      P1
P2                             PACKAGE BODY      P1

SQL> exec dbms_utility.get_dependency('PACKAGE', 'HR', 'P1');
-
DEPENDENCIES ON HR.P1
------------------------------------------------------------------
*PACKAGE HR.P1()
*   PACKAGE BODY HR.P1()
*   PACKAGE BODY HR.P2()

PL/SQL procedure successfully completed
SQL> SELECT o.object_name, o.object_type, o.status FROM user_objects o WHERE o.object_name in ('P1','P2', 'P3');
OBJECT_NAME                                                                      OBJECT_TYPE         STATUS
-------------------------------------------------------------------------------- ------------------- -------
P1                                                                               PACKAGE             VALID
P1                                                                               PACKAGE BODY        VALID
P2                                                                               PACKAGE             VALID
P2                                                                               PACKAGE BODY        INVALID
P3                                                                               PACKAGE             VALID
P3                                                                               PACKAGE BODY        VALID

6 rows selected
SQL> exec p3.write_date;
07/10/2006
PL/SQL procedure successfully completed
SQL> SELECT o.object_name, o.object_type, o.status FROM user_objects o WHERE o.object_name in ('P1','P2', 'P3');
OBJECT_NAME                                                                      OBJECT_TYPE         STATUS
-------------------------------------------------------------------------------- ------------------- -------
P1                                                                               PACKAGE             VALID
P1                                                                               PACKAGE BODY        VALID
P2                                                                               PACKAGE             VALID
P2                                                                               PACKAGE BODY        VALID
P3                                                                               PACKAGE             VALID
P3                                                                               PACKAGE BODY        VALID

6 rows selected

SQL>

Oracle stores session variables in memory. So, when package becomes invalid, firstly Oracle checks whether package has global variables. If yes, oracle unsets package state. To handle this situation you can reinitialize the package with dbms_session package or sets pragma SERIALLY_REUSABLE. Follow the examples:
Connected to Oracle Database 10g Express Edition Release 10.2.0.1.0
Connected as hr

SQL>
SQL> create table invalid(x number );

Table created
SQL> CREATE OR REPLACE PACKAGE p4 IS
  2 
  3    FUNCTION get_count RETURN NUMBER;
  4 
  5  END p4;
  6  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p4 IS
  2    k NUMBER := 3;
  3 
  4    FUNCTION get_count RETURN NUMBER IS
  5      i NUMBER;
  6    BEGIN
  7      SELECT COUNT(*) INTO i FROM invalid;
  8      k := k + 1;
  9      RETURN i + k;
 10    END get_count;
 11 
 12  END p4;
 13  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P4


SQL> exec dbms_output.put_line(p4.get_count);

4
PL/SQL procedure successfully completed
SQL> exec dbms_output.put_line(p4.get_count );
5
PL/SQL procedure successfully completed
SQL> exec dbms_utility.get_dependency( 'TABLE', 'HR', 'INVALID');
-
DEPENDENCIES ON HR.INVALID
------------------------------------------------------------------
*TABLE HR.INVALID()
*   PACKAGE BODY HR.P4()

PL/SQL procedure successfully completed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P4';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        VALID

SQL> rename invalid to invalid2;
Table renamed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P4';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        INVALID

SQL> rename invalid2 to invalid;
Table renamed
SQL> exec dbms_output.put_line( p4.get_count );
begin dbms_output.put_line( p4.get_count ); end;
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "HR.P4" has been invalidated
ORA-06508: PL/SQL: could not find program unit being called: "HR.P4"
ORA-06512: at line 1

SQL> set serveroutput on;
SQL> exec dbms_output.put_line( p4.get_count );

4
PL/SQL procedure successfully completed

SQL> CREATE OR REPLACE PACKAGE p5 IS
  2 
  3    FUNCTION get_count RETURN NUMBER;
  4 
  5  END p5;
  6  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p5 IS
  2    FUNCTION get_count RETURN NUMBER IS
  3      i NUMBER;
  4    BEGIN
  5      SELECT COUNT(*) INTO i FROM invalid;
  6      RETURN i;
  7    END get_count;
  8 
  9  END p5;
 10  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P5


SQL> exec  dbms_output.put_line(p5.get_count);

0
PL/SQL procedure successfully completed
SQL> exec dbms_utility.get_dependency( 'TABLE', 'HR', 'INVALID');
-
DEPENDENCIES ON HR.INVALID
------------------------------------------------------------------
*TABLE HR.INVALID()
*   PACKAGE BODY HR.P5()

PL/SQL procedure successfully completed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P5';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        VALID

SQL> rename invalid to invalid2;
Table renamed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P5';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        INVALID

SQL> rename invalid2 to invalid;
Table renamed
SQL> exec dbms_output.put_line( p5.get_count );
0
PL/SQL procedure successfully completed


SQL> CREATE OR REPLACE PACKAGE p6 IS
  2    PRAGMA SERIALLY_REUSABLE;
  3 
  4    FUNCTION get_count RETURN NUMBER;
  5 
  6  END p6;
  7  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p6 IS
  2    PRAGMA SERIALLY_REUSABLE;
  3 
  4    k      NUMBER := 3;
  5 
  6    FUNCTION get_count RETURN NUMBER IS
  7      i NUMBER;
  8    BEGIN
  9      SELECT COUNT(*) INTO i FROM invalid;
 10      k := k + 1;
 11      RETURN i + k;
 12    END get_count;
 13 
 14  END p6;
 15  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P6


SQL> exec  dbms_output.put_line(p6.get_count);

4
PL/SQL procedure successfully completed
SQL> exec  dbms_output.put_line(p6.get_count);
4
PL/SQL procedure successfully completed
SQL> exec dbms_utility.get_dependency( 'TABLE', 'HR', 'INVALID');
-
DEPENDENCIES ON HR.INVALID
------------------------------------------------------------------
*TABLE HR.INVALID()
*   PACKAGE BODY HR.P6()

PL/SQL procedure successfully completed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P6';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        VALID

SQL> rename invalid to invalid2;
Table renamed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P6';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        INVALID

SQL> rename invalid2 to invalid;
Table renamed
SQL> exec dbms_output.put_line( p6.get_count );
4
PL/SQL procedure successfully completed

SQL> CREATE OR REPLACE PACKAGE p7 IS
  2 
  3    FUNCTION get_count RETURN NUMBER;
  4 
  5  END p7;
  6  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY p7 IS
  2    k NUMBER := 3;
  3 
  4    FUNCTION get_count RETURN NUMBER IS
  5      i NUMBER;
  6    BEGIN
  7      SELECT COUNT(*) INTO i FROM invalid;
  8      k := k + 1;
  9      RETURN i + k;
 10    END get_count;
 11 
 12  END p7;
 13  /

Package body created
SQL> show err;
No errors for PACKAGE BODY HR.P7


SQL> exec dbms_output.put_line(p7.get_count);

4
PL/SQL procedure successfully completed
SQL> exec dbms_output.put_line(p7.get_count );
5
PL/SQL procedure successfully completed
SQL> exec dbms_utility.get_dependency( 'TABLE', 'HR', 'INVALID');
-
DEPENDENCIES ON HR.INVALID
------------------------------------------------------------------
*TABLE HR.INVALID()
*   PACKAGE BODY HR.P7()

PL/SQL procedure successfully completed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P7';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        VALID

SQL> rename invalid to invalid2;
Table renamed
SQL> SELECT  o.object_type, o.status FROM user_objects o WHERE o.object_name = 'P7';
OBJECT_TYPE         STATUS
------------------- -------
PACKAGE             VALID
PACKAGE BODY        INVALID

SQL> rename invalid2 to invalid;
Table renamed
SQL> exec  dbms_session.modify_package_state(dbms_session.reinitialize);
PL/SQL procedure successfully completed
SQL> set serveroutput on;
SQL> exec dbms_output.put_line( p7.get_count );

4
PL/SQL procedure successfully completed
SQL> --
SQL> drop table invalid;

Table dropped
SQL> drop package p7;
Package dropped