Tuesday, November 25, 2014

SQL And PL SQL Topics

PRAGMA AUTONOMOUS_TRANSACTION;
The
 AUTONOMOUS_TRANSACTION pragma instructs the PL/SQL compiler to mark a routine as autonomous (independent). An autonomous transaction is an independent transaction started by another transaction, the main transaction. Autonomous transactions let you suspend the main transaction, do SQL operations, commit or roll back those operations, then resume the main transaction.
Unlike regular triggers, autonomous triggers can contain transaction control statements such as COMMIT andROLLBACK. Also, unlike regular triggers, autonomous triggers can execute DDL statements (such as CREATEand DROP) using native dynamic SQL. Changes made by an autonomous transaction become visible to other transactions when the autonomous transaction commits. The changes also become visible to the main transaction when it resumes, but only if its isolation level is set to READ COMMITTED (the default).
1
2
3
4
5
6
7
8
CREATE TRIGGER parts_trigger
BEFORE INSERT ON parts FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO parts_log VALUES(:new.pnum, :new.pname);
COMMIT; -- allowed only in autonomous triggers
END;



Pl/SQL script for VARRAYS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
CREATE OR REPLACE PROCEDURE TEST_TYPE_VARRAY IS

  TYPE LV_ARRAY_TYPE IS VARRAY(10) OF PLS_INTEGER;
  LV_ARRAY   LV_ARRAY_TYPE := LV_ARRAY_TYPE();
  LV_ARRAY_2 LV_ARRAY%TYPE;
  /*------------------------------------------------------------------------*/
  /*initialize the lv_array using a Constructor.----------------------------*/
  /*Else 'reference to uninitialized collection is thrown-------------------------------------*/
  /*-----------------------------------------------------------------------------------------------*/
BEGIN
  FOR I IN 1 .. 10 LOOP
    LV_ARRAY.EXTEND;
    /*-------------------------------------------------------------*/
    /*EXTEND is required to add elements to the varray. -----------*/
    /*Should not exceed size of VARRAY-----------------------------*/
    /*----------------------------------------------------------------------------*/
    LV_ARRAY(I) := I;
  END LOOP;
  LV_ARRAY_2 := LV_ARRAY_TYPE(11, 12, 13, 14, 15);
  DBMS_OUTPUT.PUT_LINE('LV_ARRAY');
  FOR I IN LV_ARRAY.FIRST .. LV_ARRAY.LAST LOOP
    DBMS_OUTPUT.PUT_LINE(LV_ARRAY(I));
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('------------------------------');
  /*--------------------------------------------------------------------------------------------------*/
  /*Prints 1,2,3,4,5,6,7,8,9,10.---------*/
  /*--------------------------------------------------------------------------------------------------*/
  DBMS_OUTPUT.PUT_LINE('LV_ARRAY_2');
  FOR I IN LV_ARRAY_2.FIRST .. LV_ARRAY_2.LAST LOOP
    DBMS_OUTPUT.PUT_LINE(LV_ARRAY_2(I));
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('------------------------------');
  /*----------------------------------------------------------------------------------*/
  /*Prints 11,12,13,14,15---------*/
  /*------------------------------------------------------------------------------------*/
  LV_ARRAY_2.EXTEND;
  /*-------------------------------------------------------------------------------------------*/
  /*EXTEND adds a  null element .COUNT is now 6, after EXTEND. ----------*/
  /* EXTEND(2) will add two null elements------------------------------------------*/
  /*-------------------------------------------------------------------------*/
  LV_ARRAY_2(LV_ARRAY_2.COUNT) := 16;
  DBMS_OUTPUT.PUT_LINE('LV_ARRAY_2 after extend');
  FOR I IN LV_ARRAY_2.FIRST .. LV_ARRAY_2.LAST LOOP
    DBMS_OUTPUT.PUT_LINE(LV_ARRAY_2(I));
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('------------------------------');
  /*---------------------------------------------------------------------*/
  /*Prints 11,12,13,14,15,16---------*/
  /*-------------------------------------------------------------*/

  DBMS_OUTPUT.PUT_LINE('The maximum limit of a VARRAY can be displayed using LIMIT' ||
                       '---' || LV_ARRAY.LIMIT);
  DBMS_OUTPUT.PUT_LINE('FIRST and LAST displays the smallest and largest INDICES, not the elements' ||
                       '---' || LV_ARRAY_2.FIRST || '---' ||
                       LV_ARRAY_2.LAST);
  DBMS_OUTPUT.PUT_LINE('For VARRAYS, COUNT is always equal to LAST' ||
                       '--LAST INDEX--' || LV_ARRAY_2.LAST || '--COUNT--' ||
                       LV_ARRAY_2.COUNT);

  DBMS_OUTPUT.PUT_LINE('TRIM can be used to remove the last element from the VARRAY');
  DBMS_OUTPUT.PUT_LINE('Before TRIM, the last element is' || '----' ||
                       LV_ARRAY(LV_ARRAY.LAST));
  LV_ARRAY.TRIM;
  DBMS_OUTPUT.PUT_LINE('After TRIM, the last element is' || '----' ||
                       LV_ARRAY(LV_ARRAY.LAST));
  DBMS_OUTPUT.PUT_LINE('Before TRIM, the last element of lv_array_2 is' ||
                       '----' || LV_ARRAY_2(LV_ARRAY_2.LAST));
  LV_ARRAY_2.TRIM(2);
  DBMS_OUTPUT.PUT_LINE('After TRIM(2), the last element of lv_array_2 is' ||
                       '----' || LV_ARRAY_2(LV_ARRAY_2.LAST));
  LV_ARRAY_2.DELETE;
  DBMS_OUTPUT.PUT_LINE('DELETE removes all elements from a VARRAY' ||
                       '---lv_array_2.COUNT-->' || LV_ARRAY_2.COUNT);
  /*----------------------------------------------------------------------------------*/
  /*VARRAYs are dense------------------------------------------------------*/
  /*so we cannot use DELETE(2) unlike in associative arrays or nested tables */
  /*---------------------------------------------------------------------------------*/

END;


REF CURSOR example in PL/SQL

The following script shows how to use REF CURSOR, how to open a strong REF CURSOR and a weak REF CURSOR.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
CREATE OR REPLACE PROCEDURE TEST_REF_CURSOR1 IS

-------------------procedure to test the use of ref_cursor

  LV_VAR               EMPLOYEES%ROWTYPE;
  LV_EXCHANGE     EMP_DETAIL%ROWTYPE;

  TYPE TEST_REF IS REF CURSOR;--weak ref cursor
  REF_EMP            TEST_REF;

  TYPE test_strong_ref IS REF CURSOR RETURN TEMP_TBL_TEST%ROWTYPE;
/*strong ref cursor using %rowtype.*/
  strong_ref          test_strong_ref;
  lv_strong             strong_ref%ROWTYPE;

   lv_var_type ANOTHER_TABLE%ROWTYPE;
   TYPE test_strong_ref_type IS REF CURSOR RETURN lv_var_type%TYPE;--strong ref cursor using %type.
   strong_ref_type       test_strong_ref_type;

BEGIN
  OPEN REF_EMP FOR
    SELECT * FROM EMPLOYEES WHERE ROWNUM < 10;---USE OF WEAK CURSOR
  LOOP
    FETCH REF_EMP
      INTO LV_VAR;
    EXIT WHEN REF_EMP%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(LV_VAR.EMPLOYEE_NUMBER || '----' ||
                         LV_VAR.EMPLOYEE_NAME);
  END LOOP;
  CLOSE REF_EMP;---no need  close before opening the ref cursor again.

  IF (REF_EMP%ISOPEN) THEN
    DBMS_OUTPUT.PUT_LINE('cursor open');
  ELSE
    BEGIN
      OPEN REF_EMP FOR
        SELECT * FROM EMP_DETAIL WHERE ROWNUM < 10;
      LOOP
        FETCH REF_EMP
          INTO LV_EXCHANGE;
        EXIT WHEN REF_EMP%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(LV_EXCHANGE.EMP_HIRE_DATE || '----' ||
                             LV_EXCHANGE.EMP_DEPT);
      END LOOP;
      CLOSE REF_EMP;
    END;
  END IF;

OPEN strong_ref FOR SELECT * FROM TEMP_TBL_TEST WHERE rownum < 10;
    LOOP
      FETCH  strong_ref INTO lv_strong;
      EXIT WHEN strong_ref%NOTFOUND;
      dbms_output.put_line(lv_strong.COL1||'--'||lv_strong.COL2);
    END LOOP;
   CLOSE strong_ref;

   OPEN strong_ref FOR SELECT * FROM TEMP_TBL_TEST_DUPLICATE WHERE rownum < 10;/*can be used as both the tables
   have the same structure. (TEMP_TBL_TEST_DUPLICATE CREATED FROM TEMP_TBL_TEST)*/
   LOOP
     FETCH strong_ref INTO lv_strong;
       EXIT WHEN strong_ref%NOTFOUND;
      dbms_output.put_line(lv_strong.COL1  ||'---'||lv_strong.COL2 );
    END LOOP;
   CLOSE strong_ref;

   OPEN strong_ref_type FOR SELECT * FROM ANOTHER_TABLE WHERE rownum < 4;
   LOOP
      FETCH strong_ref_type INTO lv_var_type;
         EXIT WHEN strong_ref_type%NOTFOUND;
         dbms_output.put_line(lv_var_type.COL1);
      END LOOP;
     CLOSE strong_ref_type;

END;







'&' vs '&&' in ORACLE

“&” is used to create a temporary substitution variable. You will be prompted to enter the value every time the variable is referenced.
“&&” is used to create a permanent substitution variable. You need to enter the value only once.
1. Using ‘&’
1
2
SELECT employee_name FROM employee
    WHERE emp_id ='&emp'
Every time, you execute this select statement, you will have to pass the value of emp.
2. Using ‘&&’
Consider the following procedure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQL> set serveroutput on;
SQL> CREATE OR REPLACE PROCEDURE test_substitution AS
   emp_name VARCHAR2(10);
   emp_designation VARCHAR2(10);
   BEGIN
   SELECT employee_name INTO emp_name FROM employee
   WHERE emp_id ='&&emp';
   dbms_output.put_line(emp_name);
   SELECT employee_desg INTO emp_designation FROM employee
   WHERE emp_id='&emp';
   dbms_output.put_line(emp_designation);
   END;

Procedure created
This procedure fetches the employee_name and employee_desg from the employee table for a particular emp_id.
It will prompt for the emp_id only once as ‘&&emp’ is used. In the next select statement,although a substitution variable is used, you will not be prompted to enter a value for emp.
The next time you execute the procedure,it will not prompt for the value to be passed.
If you need to pass a different value each time you run the procedure, then you will have to undefine the variable emp.
1
SQL> undefine emp;
AND then compile the procedure again. This will prompt for a new value to be passed.
If you just undefine the variable and execute the procedure, it will not prompt for a new value.
It is important to
 undefine and then recompile the procedure.
What is a one-time-only procedure in pl/sql?

A one-time-only procedure is an anonymous block of code encapsulated within a package body, used for initializing variables and is executed only once when the package is invoked for the first time in a user session.
It has a BEGIN but not an END :) . The END of the package body serves as the end of the one-time-only procedure within the package body.
An example of a one-time-procedure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE OR REPLACE PACKAGE pckg_name
AS
   FUNCTION  function_name ... ;
   PROCEDURE procedure_name ... ;
END pckg_name

CREATE OR REPLACE PACKAGE BODY pckg_name
AS
FUNCTION  function_name BEGIN ... END function_name;
PROCEDURE procedure_name ... BEGIN ... END procedure_name;
BEGIN
-- whatever code is here executes one time only,
-- the first time the package is called
END pckg_name











The 'RR' Date Format

The RR date format element of the TO_DATE and TO_CHAR functions allows a database site to default the century to different values depending on the two-digit year, so that years 50 to 99 default to 19xx and years 00 to 49 default to 20xx. Therefore, regardless of the current century at the time the data is entered, the ‘RR’ format will ensure that the year stored in the database is as follows:
·         If the current year is in the second half of the century (50 – 99), and a two-digit year between `00′ and `49′ is entered, this will be stored as a ‘next century’ year. For example, `02′ entered in 1996 will be stored as `2002′
·         If the current year is in the second half of the century (50 – 99), and a two-digit year between `50′ and `99′ is entered, this will be stored as a ‘current century’ year. For example, `97′ entered in 1996 will be stored as `1997′.
·         If the current year is in the first half of the century (00 – 49), and a two-digit year between `00′ and `49′ is entered, this will be stored as a ‘current century’ year. For example, `02′ entered in 2001 will be stored as `2002′.
·         If the current year is in the first half of the century (00 – 49), and a two-digit year between `50′ and `99′ is entered, this will be stored as a ‘previous century’ year. For example, `97′ entered in 2001 will be stored as `1997′.
The `RR’ date format is available for inserting and updating DATE data in the database. It is not required for retrieval or query of data already stored in the database as Oracle has always stored the YEAR component of a date in its four-digit form.
Here is an example of the RR usage:
INSERT INTO emp (empno, deptno,hiredate) VALUES
   (9999, 20, TO_DATE('01-jan-03', 'DD-MON-RR'));
INSERT INTO emp (empno, deptno,hiredate) VALUES
    (8888, 20, TO_DATE('01-jan-67',  'DD-MON-RR'));
SELECT empno, deptno,
   TO_CHAR(hiredate, 'DD-MON-YYYY') hiredate
FROM emp;
This produces the following data:
EMPNO DEPTNO HIREDATE
———- ———- —————–
8888 20 01-JAN-1967
9999 20 01-JAN-2003





Rollup in GROUP BY (ORACLE)

Level: Intermediate.
ROLLUP is used to calculate multiple levels of subtotals for a specified group. It also calculates a grand total.
It is an extension of the GROUP BY CLAUSE.
It is very helpful for subtotaling along a hierarchical dimension such as time or geography. For instance, a query could specify a ROLLUP(y, m, day) or ROLLUP(country, state, city). It can be used to find the MIN, MAX,AVG,SUM and COUNT of specified sub groups.
Consider the following table and the sql statements below:
Table used:
CLASS_NUM
STUDENT_ID
1
1
1
2
1
3
2
1
2
2
3
1
4
1

————————————–
1
2
SELECT class_num,SUM(class_num) FROM temp_1234
GROUP BY class_num---sum of individual class_num
This is a simple group by clause with the aggregate function SUM.
1
2
SELECT class_num,SUM(class_num) FROM temp_1234
GROUP BY rollup(class_num)--sum of individual groups and the sum of sum(class_num)
An additional row is displayed, which shows the SUM of SUM(class_num)
———————————————————————————————————
1
2
SELECT class_num,COUNT(*) FROM temp_1234
GROUP BY class_num--how many times different class_num occours.

1
2
SELECT class_num,COUNT(*) FROM temp_1234
GROUP BY rollup(class_num)--how many times different class_num occurs and sum of count(*)
———————————————————————————————————
1
2
SELECT class_num,student_id,COUNT(*) FROM temp_1234
GROUP BY class_num,student_id---count(*) gives count of occurrence each group by of (class_num,student_id)

1
2
SELECT class_num,student_id,COUNT(*) FROM temp_1234
GROUP BY rollup(class_num,student_id)---count(*) gives count of each group by of (class_num,student_id) and sum of group by of each (class_num and student_id. Rollup on switch type. Since more than two columns in group by, a break up of count(*) is also shown.

1
2
3
SELECT class_num,student_id,AVG(student_id),SUM(student_id),MIN(student_id),max(student_id)
FROM temp_1234
GROUP BY ROLLUP(class_num,student_id)---follows the same concept as above and extended for max, min,sum and avg

1
2
SELECT student_id,class_num,COUNT(*) FROM temp_1234
GROUP BY rollup(student_id,class_num)---count(*) gives count of each group by of (student_id,class_num) and sum of group by of each (class_num and student_id. Rollup on student_id.
———————————————————————————————————
1
2
SELECT student_id,class_num,AVG(student_id),SUM(student_id),MIN(student_id),max(student_id)
FROM temp_1234 GROUP BY ROLLUP(student_id,class_num)---follows the same concept as above and extended for max, min,sum and avg. Rollup on student_id
——————more on rollup——————-
——————find the amount for each date and the total amount for all the dates
1
2
SELECT purchase_date,SUM(amount) FROM test_rollup
GROUP BY ROLLUP(purchase_date)
——————-find the amount spent by each cust_id on each day,
the total amount for each day and the total amount for all the dates.*/
start with
1
2
3
SELECT purchase_date,cust_id,SUM(amount)
FROM test_rollup
GROUP BY purchase_date,cust_id
roll it up
1
2
3
SELECT purchase_date,cust_id,SUM(amount)
FROM test_rollup
GROUP BY ROLLUP(purchase_date,cust_id)
——————-more columns…
1
2
3
SELECT purchase_date,cust_id,order_id,SUM(amount)
FROM test_rollup
GROUP BY ROLLUP(purchase_date,cust_id,order_id)





Calling a procedure with IN and OUT parameter in oracle

This post shows how one can call a procedure with an IN and OUT parameter from the sql prompt.
Consider the following procedure with one IN parameter and one OUT parameter.
1
2
3
4
5
6
7
8
CREATE OR REPLACE PROCEDURE test_in_out(num1 IN NUMBER,num2 OUT NUMBER)
AS
num3 NUMBER;
BEGIN
   SELECT SUM(2+3+4) INTO num2 FROM dual;
   num3:=num1;
   dbms_output.put_line('value of num3 is-->'||num3);
 END;

First declare a bind variable as
1
SQL> variable T number;
Next set serveroutput on. Otherwise dbms_output will not be able to write messages at the prompt.
1
SQL> set serveroutput on;
Then call the procedure as :
1
2
3
4
5
6
7
8
9
10
SQL> exec test_in_out(3, :T);
value of num3 is-->
3

PL/SQL procedure successfully completed
T
---------
9

SQL>
To see the value of T you can use the PRINT command.
1
2
3
4
SQL> print T
T
---------
9



Pseudocolumns in Oracle

There are five pseudocolums that can be used in PL/SQL. They are CURRVAL, NEXTVAL, LEVEL, ROWID
and
 ROWNUM.
1. CURRVAL and NEXTVAL
CURRVAL returns the current value in a specified sequence. NEXTVAL increments the sequence and returns the
next value. If you have a sequence GENERATE_NUM, then you can obtain the current value or the next value as

SELECT GENERATE_NUM.CURRVAL FROM DUAL;
SELECT GENERATE_NUM.NEXTVAL FROM DUAL;
Note: Before you can reference CURRVAL in a session, you have to use NEXTVAL to generate a number.
2. ROWID
ROWID returns the binary address of a row in the database table. It can be stored in variables of type UROWID to store rowids in a readable format.
SQL>
SQL> DECLARE
2 u_row_id urowid;
3 BEGIN
4 SELECT ROWID INTO u_row_id FROM table_name
5 WHERE ROWNUM<2 span="">
6 dbms_output.put_line(u_row_id);
7 END;
8 /
AAARwiAAKAABcnKAAA
PL/SQL procedure successfully completed
3. ROWNUM
ROWNUM returns a number indicating the order in which a row was selected from a table. The first row has a ROWNUM of 1.
SQL>
SQL> SELECT ROWNUM FROM table_name
2 WHERE ROWNUM<10 span="">
ROWNUM
----------
1
2
3
4
5
6
7
8
9
9 rows selected
SQL>

4. LEVEL
LEVEL is used with the SELECT CONNCET BY statement to organize rows in a tree structure. Root level is 1,
children is 2 and so on. The START WITH clause identifies the root of the tree. The direction is specified
with the PRIOR operator.
Consider the following employee table
EMPLOYEEID EMPLOYEENAME REPORTING_EMPLOYEE_ID
845628 AFREF RTRWD 971368
345678 MD. KHADEMUL ISLAM 789456
123456 SAMTRA DERGT 718293
789456 DILIP KR. BARMAN 123639
123639 PULIN BIHARI PAL 852369
852369 A.K.NARJINERY 198753
select level, employeeid,employee_name,manger_employee_id
from employee Start with employeeid ='345678'
connect by prior manger_employee_id = employeeid
order by LEVEL
And the output is:
LEVEL EMPLOYEEID EMPLOYEENAME REPORTING_EMPLOYEE_ID
1 345678 MD. KHADEMUL ISLAM 789456
2 789456 DILIP KR. BARMAN 123639
3 123639 PULIN BIHARI PAL 852369
4 852369 A.K.NARJINERY 198753