September 6, 2010

Performance Improvement with Associative Arrays in PL/SQL, Oracle

Posted in Oracle, PL/SQL, SQL tagged , , , , , , at 10:41 am by itsourteamwork

Lets see, how the performance can be improved with the use of Associative Arrays.

We are going using Associative Arrays for caching the data to reduce the function calls or select statements.

Suppose, there is a Function (get_dept_info) which returns the dept name for a given dept no from the dept table.

In a main pl/sql block, from the emp table, all the employees will be retrieved one by one in a loop, for each emp retrieved, the above function (get_dept_info) will be called to get the dept name (we can get the dept name by joining the emp table directly with dept table also). For explaining the concept only we are using this example.

The algorithm that we are going to follow is

  1. Create a replica table (emp_new) for the emp table
  2. Insert more data into the table (emp_new) so for the testing.
  3. Create a pl/sql block
  4. In the pl/sql block, loop thru the employees in the emp_new table
  5. for each emp, try to get the dept name :
  6. This part we will do in two ways
  7. 1: directly calling (get_dept_info) function
  8. 2: using associative arrays
  9. then do any processing as per the requirement.  In our case we will not do anything.

Now lets how to do this:

–Connect to the user Scott

SQL> SHOW USER;
USER is “SCOTT”

–Set the TIMI ON to see the time taken to execute the commands

SQL> SET TIMI ON;

–Create a table, exact replica of emp table

SQL> CREATE TABLE emp_new AS SELECT * FROM emp;

Table created.

Elapsed: 00:00:00.10

–Change the column width of empno to store any bigger no.

SQL> ALTER TABLE emp_new MODIFY empno NUMBER;

Table altered.

Elapsed: 00:00:00.03

–A pl/sql block to insert more rows into the emp_new table

SQL> BEGIN
2    FOR I IN 1..15 LOOP
3      INSERT INTO emp_new SELECT * FROM emp_new;
4    END LOOP;
5    UPDATE emp_new SET empno = empno+ROWNUM;
6    COMMIT;
7  END;
8
9  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:22.28
SQL>
SQL> SELECT COUNT(*) FROM emp_new;

COUNT(*)
———-
491520

Elapsed: 00:00:01.23

–Create the function to return the dept name for a given dept no

SQL> CREATE OR REPLACE    FUNCTION get_dept_info ( p_dept_no dept.deptno%TYPE) RETURN VARCHAR2 IS
2        v_dname  dept.dname%TYPE;
3      BEGIN
4        SELECT dname INTO v_dname FROM dept WHERE deptno = p_dept_no;
5        RETURN v_dname;
6      EXCEPTION
7        WHEN OTHERS THEN
8          Raise_application_error (-20100,’Error ‘|| sqlerrm ||’ for deptno = ‘ || p_dept_no);
9      END get_dept_info ;
10
11  /

Function created.

Elapsed: 00:00:00.06

–Now, execute the following code to get the dept name for the dept no

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2      v_str                            VARCHAR2(32000):=’In Declaration Secion’;
3      v_dname                          dept.dname%TYPE ;
4
5  BEGIN
6    v_str :=’Before For loop at 100′;
7    FOR emp_data IN (SELECT * FROM emp_new WHERE deptno IS NOT NULL)  LOOP –loop thru the employees
8      v_str :=’Calling get_dept_info (‘ ||emp_data.deptno ||’) at 200′;
9      v_dname := get_dept_info (emp_data.deptno);  –function call to get the dname
10      –DBMS_OUTPUT.PUT_LINE(‘For dno ‘|| emp_data.deptno ||’ dname is ‘|| v_dname);
11      NULL;
12    END LOOP;
13
14
15  EXCEPTION
16    WHEN OTHERS THEN
17       Raise_application_error(-20010,’Error ‘||SQLERRM ||’ at ‘||v_str);
18  END;
19
20  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:14.57

–Change the code to get the dept name using associative arrays

–Create an associative array index by VARCHAR2;

–FOR each dept no retrieved from the emp table, first check in the associative arrary, if there exists a dept name

–If exists then return

–If not then, NO_DATA_FOUND exception will be raised

–In the exception handler, call the function (get_dept_info) to get the dept nam

–store the dept name in the associative array for the next use

–In this way, the no of calls to the function will be reduced(the max no of  calls will be the no of distinct values in the emp table)

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2      v_str                            VARCHAR2(32000):=’In Declaration Secion’;
3      v_dname                          dept.dname%TYPE ;
4      TYPE  dname_type  IS TABLE OF dept.dname%TYPE INDEX BY VARCHAR2(10);  –Array type declaration
5      v_dname_tab  dname_type;  –Variable for the array
6  BEGIN
7    v_str :=’Before For loop at 100′;
8    FOR emp_data IN (SELECT * FROM emp_new WHERE deptno IS NOT NULL)  LOOP
9      BEGIN
10        v_dname := v_dname_tab(emp_data.deptno); — Check associative array
11      EXCEPTION
12        WHEN NO_DATA_FOUND THEN  –Means the dept no is not there in the array
13          v_str :=’Calling get_dept_info (‘ ||emp_data.deptno ||’) at 200′;
14          v_dname := get_dept_info (emp_data.deptno);   –call the function to get the dept name
15          v_dname_tab(emp_data.deptno ) := v_dname ;  –Store in the associative array for the future use
16      END;

17
18      –DBMS_OUTPUT.PUT_LINE(‘For dno ‘|| emp_data.deptno ||’ dname is ‘|| v_dname);
19      NULL;
20    END LOOP;
21
22
23  EXCEPTION
24    WHEN OTHERS THEN
25       Raise_application_error(-20010,’Error ‘||SQLERRM ||’ at ‘||v_str);
26  END;
27
28  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.76

The time taken for first block to execute is 14 secs. The time taken for the second block to execute is 2 secs. See the diff.

August 3, 2010

Not In Vs Minus in SELECT statement of Oracle

Posted in Oracle, SQL tagged , , at 12:56 pm by itsourteamwork

What is the diff between NOT IN and MINUS. As per the SET theory, both should yield the same results.

Lets take an example : From the scott schema by using the Emp and Dept table, if the following queries were fired to get the list of DEPARTMENTS that do not have any EMPLOYEE, then,

SELECT deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp);

and

SELECT deptno FROM dept
MINUS
SELECT deptno FROM emp;

both queries will return no rows.
————————————————————–
Now, lets look at the following data:

DEPARTMENT table got few more rows

DEPTNO DNAME LOC
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
50 QUALITY CHICAGO
60 IT CHICAGO
70 MARKETING NEY YORK

EMPLOYEE table
For the first row, employee no 9999, there is no department assigned. Deptno is NULL.

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO STATUS
9999 James James 7782 23-Jan-82 1300 (null) (null) Y
7369 SMITH CLERK 7902 17-Dec-80 800 (null) 20 Y
7499 ALLEN SALESMAN 7698 20-Feb-81 1600 300 30 Y
7521 WARD SALESMAN 7698 22-Feb-81 1250 500 40 Y
7566 JONES MANAGER 7839 2-Apr-81 2975 (null) 20 Y
7654 MARTIN SALESMAN 7698 28-Sep-81 1250 1400 50 N
7698 BLAKE MANAGER 7839 1-May-81 2850 (null) 30 Y
7782 CLARK MANAGER 7839 9-Jun-81 2450 (null) 10 N
7788 SCOTT ANALYST 7566 19-Apr-87 3000 (null) 20 Y
7839 KING PRESIDENT (null) 17-Nov-81 5000 (null) 10 N
7844 TURNER SALESMAN 7698 8-Sep-81 1500 0 30 Y
7876 ADAMS CLERK 7788 23-May-87 1100 (null) 20 N
7900 JAMES CLERK 7698 3-Dec-81 950 (null) 30 Y
7902 FORD ANALYST 7566 3-Dec-81 3000 (null) 20 N
7934 MILLER CLERK 7782 23-Jan-82 1300 (null) 60 Y

If the below queries were fired, to get the list of departments that does not have any employee, one row (department no 70) should be returned.

SELECT deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp);

and

SELECT deptno FROM dept
MINUS
SELECT deptno FROM emp;

But surprisingly, first query will return no rows and the second will return the department no 70 because this dept does not have any employee.

Why? Its because, in the first query, each deptno from dept table will be compared with all the deptnos along with the NULL in the emp table. Remember, comparison of a value with NULL will be FALSE. So the deptno 70 comparison with NULL will be false and deptno 70 will not be returned.

June 1, 2010

Recursive WITH Clause in Oracle 11g

Posted in Oracle, SQL tagged , , at 1:41 pm by itsourteamwork

Oracle Provided a new command in SELECT syntax. Following is what is mentioned in the help

——————————————————
The SQL WITH clause has been extended to enable formulation of recursive queries.

Recursive WITH clause complies with the American National Standards Institute (ANSI) standard. This makes Oracle ANSI-compatible for recursive queries.

For More info Click Here

Happy Learning

December 25, 2009

New Features in Oracle 11g : Caching and Pooling

Posted in Oracle, PL/SQL, SQL tagged , , , at 1:45 am by itsourteamwork

Another good article on Caching and Pooling.

I was looking for the caching feature in 2005 as one of my select statement used to call a function more then 5 times with the same parameters. Any how “Better late than never”

December 20, 2009

Logging Vs Nologging in Oracle

Posted in Oracle, SQL tagged , at 12:40 pm by itsourteamwork

I want to share my experience in using Nologging. I am not going into the details like what is logging/nologging mode, what happens in logging/nologging mode etc, because there are many sites available in net, which explains about that. I just want to share my observations.

In one of my projects, I had load millions of data from one table to another table. The table got approximately 50 columns. Here the requirement is load the data and commit it. Without nologging, the insert statement took more than 8 hours to complete.

I have altered the destination table into nologging mode and inserted the data. Added APPEND hint to the insert statement. The insert statement execution completed in less than 3 hours.

Logging Vs Nologging

Once the required operation is completed its better to turn on the logging mode.

How to find whether a table is in logging mode or no logging mode? Following Select statement will display the table name and logging mode

SELECT table_name, logging FROM user_tables;

December 4, 2009

New Features in Oracle 11g : SQL Operations: Pivot and Unpivot

Posted in Oracle, SQL tagged , , at 10:22 am by itsourteamwork

Recently I came across the Pivot and Unpivot functions in Oracle 11g. Here is the link

September 19, 2009

LISTAGG Function : New function in Oracle 11gR2

Posted in Oracle, SQL tagged , , , at 9:09 am by itsourteamwork

Prior to the release of Oracle 11gR2, if the list of employees needs to be displayed as ‘Semi-colon’ separated values along with the department id, the following SQL was used

 SELECT t.department_id Dept
             , MAX(SYS_CONNECT_BY_PATH(first_name,’ ;’)) as Employees
   FROM (SELECT department_id
                     , first_name
                     , ROW_NUMBER() OVER (PARTITION BY department_id
                                                            ORDER BY department_id
                                                                        , first_name) rn

              FROM ( SELECT DISTINCT department_id , first_name
                           FROM employees)) t

   START WITH t.rn = 1

CONNECT BY t.rn = PRIOR t.rn + 1 
             AND department_id = prior department_id

    GROUP BY department_id;

Output

Dept

Employees

10

; Jennifer

20

; Michael; Pat

30

; Alexander; Den; Guy; Karen; Shelli; Sigal

40

; Susan

50

; Adam; Alana; Alexis; Anthony; Britney; Curtis; Donald; Douglas; Girard; Hazel; Irene; James; Jason; Jean; Jennifer; John; Joshua; Julia; Kelly; Kevin; Ki; Laura; Martha; Matthew; Michael; Mozhe; Nandita; Payam; Peter; Randall; Renske; Samuel; Sarah; Shanta; Stephen; Steven; TJ; Timothy; Trenna; Vance; Winston

60

; Alexander; Bruce; David; Diana; Valli

70

; Hermann

80

; Alberto; Allan; Alyssa; Amit; Charles; Christopher; Clara; Danielle; David; Eleni; Elizabeth; Ellen; Gerald; Harrison; Jack; Janette; John; Jonathon; Karen; Lindsey; Lisa; Louise; Mattea; Nanette; Oliver; Patrick; Peter; Sarath; Sundar; Sundita; Tayler; William

90

; Lex; Neena; Steven

100

; Daniel; Ismael; John; Jose Manuel; Luis; Nancy

110

; Shelley; William

 

; Kimberely

Now, Oracle 11gR2, Oracle provided a columnar function called LISTAGG

Syntax:

LISTAGG (measure_column_expr [, ‘delimiter’])

WITHIN GROUP (order_by_clause) [OVER query_partition_clause]

This LISTAGG function flattens the results into a single record by combining the values. This function can be used in one-to-many constructions.

One of the fun little features – to switch gears here – that is added to SQL is in analytics. We now have something called LISTAGG. LISTAGG allows you to flatten a result set into a single record. This is often

used in 1:many constructions such as hierarchical data and bill of materials.

The above output can be achieved by

SELECT department_id ,

   LISTAGG(last_name, ‘; ‘)

WITHIN GROUP (ORDER BY hire_date) "Employees"

FROM employees

GROUP BY department_id;

For more info, click here