Showing posts with label SQL & PLSQL. Show all posts
Showing posts with label SQL & PLSQL. Show all posts

Wednesday, 24 February 2016

Cursors in plsql

CURSOR : A cursors is a pointer used to fetch rows from a result set 
Two types of classification s:


I.STATIC CURSOR S: 
Static : Normal cursor (implicit or explicit)

Cursor attributes  for implicit and explicit:

%FOUND - records fetched successfully
%NOTFOUND - no records fetched
%ROWCOUNT - Number of records fetched
%ISOPEN - returns TRUE if cursor is open

a. Implicit : 
Cannot be opened outside the statement
More fast and less coding effort.
Will never raise INVALID_CURSOR error
Raises NO_DATA_FOUND and TOO_MANY_ROWS exceptions (eg: select <stmt>)

Example Implicit Cursor:

select * from emp

If SQL%FOUND then

v_count:= SQL%ROWCOUNT

end if;


b. Explicit : 2 network round trips. Store data first then retrieve data. 
More programmatic control.
Programmer could open; fetch data, close, check attributes etc.

Syntax:
open c1; -- cursor c1 is select <stmt>

fetch <>

exit when c1%NOTFOUND

Example Explicit cursor:

Without Using Loop s
Declare

Cursor cur1 is

select ename,empno,sal from emp

where sal<50000 and deptno=50

begin

open cur1;

fetch cur1 into v_ename,v_empno,v_sal;

exit when cur1%notfound;

---<do processing>

close cur1;

end;

Using Loops:

Declare

Cursor cur1 is

select ename,empno,sal from emp

where sal<50000 and deptno=50

begin

For rec in cur1
loop
dbms_output.put_line('Employee Number '||rec.empno);
end loop;

end;

Using Loops with Cursor Parameters:

Declare

Cursor cur1( cp_deptNo Number) 
is
select ename,empno,sal from emp
where sal<50000 and deptno=cp_deptNo

l_deptNo Number :=50;
begin

For rec in cur1(l_deptNo)
loop
dbms_output.put_line('Employee Number '||rec.empno);
end loop;

end;

II. DYNAMIC CURSOR s : 

Oracle REF CURSOR Types:
With the REF_CURSOR you can return a recordset/cursor from a stored procedure
(i.e Ref Cursors can have Record/s as return types.)
Could be declared once and defined many times in different procedures. 

a)Strong : For the strong ref cursor the returning columns with data type and length need to be known at compile time.
b)Weak :For the weak ref cursor the structure does not need to be known at compile time.

Example For the Ref Cursor :


--SPECK PACKAGE 
CREATE OR REPLACE PACKAGE REFCURSOR_PKG
 AS
  TYPE WEAK_REF_CURSOR IS REF CURSOR; -- Until 9i
  TYPE STRONG_REF_CURSOR IS REF CURSOR RETURN EMP%ROWTYPE;

END REFCURSOR_PKG;

The pl/sql procedure that returns a ref-cursor looks like this:


--BODY PACKAGE 
CREATE OR REPLACE PACKAGE BODY REFCURSOR_PKG
AS
-- For Weak Ref Cursor: 
PROCEDURE 
WEAK_REF_CUR_PRC( p_deptno IN number,
                  p_cursor OUT REFCURSOR_PKG.WEAK_REF_CURSOR -- Until 9i
                  ---- From 9i (p_cursor OUT SYS_REFCURSOR )----
                 )
IS

BEGIN

  OPEN p_cursor FOR
  SELECT *  FROM   emp
  WHERE  deptno = p_deptno;
end WEAK_REF_CUR_PRC;

-- For Strong Ref Cursor: 
PROCEDURE 
STRONG_REF_CUR_PRC( p_deptno IN number,
                    p_cursor OUT REFCURSOR_PKG.STRONG_REF_CURSOR
                  )
IS

BEGIN
  SELECT *  FROM   emp
  WHERE  deptno = p_deptno;
  end STRONG_REF_CUR_PRC;
 END REFCURSOR_PKG;

ERRBUF and RETCODE In Concurrent Program

For each Concurrent program we must use mandatory parameters : ERRBUF and RETCODE.

ERRBUF: It return the error message. 
For you program if you get any error in exception block you can assign the error message to this parameter. 
This error message you can see after concurrent program run go to details button it will open details in that Completion Text filed will show your errbuf.

RETCODE: This parameter returns the status of the concurrent program.
0- Success --Completed
1- Warning -- Yellow color
2- Error -- Red

These parameters we call as a first parameters for the program.

Ex:
Create procedure  CProgram(ERRBUF out varchar2, RETCODE  out varchar2, person_id in NUmber)
as
begin
<Declaration Goes  Here>
begin
<Code Block1 Here>

exception
when no_data_found then
retcode := 1;
errbuf:= 'RetCode : Warning';
end;
...
<Code Block2 Here>
...
retcode:= 0;
commit;
exception
when others then

retcode := 2;

errbuf:= 'errbuf:= 'RetCode : Error';

end;

Bulk Collect In Oracle PLSQL:

We usually use cursor for loops to process data.(i.e declare a cursor, open it, fetch from it row by row in a loop and process the row they fetch) statements in plsql programs causes a context switch between the plsql engine and the sql engine.Too many context switches may degrade performance dramatically.

In order to reduce the number of these context switches we can use bulk collecting feature
Bulk collecting lets us to transfer rows between the sql engine and the plsql engine as collections.
Bulk collecting is available for select, insert, delete and update statements.

Below are some examples:

create table BULK_COLLECT_TEST as select * from PER_ALL_PEOPLE_F;

Table created.

insert into BULK_COLLECT_TEST

select * from BULK_COLLECT_TEST;

20000 rows created.

--BLOCK1:Using Loops
declare
 cursor c1
 is select object_name from BULK_COLLECT_TEST;
 rec1 c1%rowtype;
 begin
      open c1;
       loop
       fetch c1 into rec1;
    exit when c1%notfound;
    null;
    end loop;
 end;

total Elapsed Time is : 45 Secs

--BLOCK2: Using Bulk Collecting
declare
  cursor c1 is select object_name from BULK_COLLECT_TEST;
  type c1_type is table of c1%rowtype;
  rec1 c1_type;
begin
open c1;
   fetch c1 bulk collect into rec1;
end;

total Elapsed Time is : 5 Sec

So bulk collecting the rows shows a huge performance improvement over fetching row by row.

Some cases there are many rows to process, we can limit the number of rows to bulk collect, process those rows and fetch again.
Otherwise process memory gets bigger and bigger as you fetch the rows.

--Bulk Collect Example using LIMIT :
declare
 cursor c1 is select object_name from BULK_COLLECT_TEST;
 type c1_type is  table of c1%rowtype;
 rec1 c1_type;
begin
    open c1;
    loop
    fetch c1 bulk collect into rec1 limit 200;
    for i in 1..rec1.count loop
    null;
    end loop;
    exit when c1%notfound;
    end loop;
end;

Tuesday, 9 February 2016

Decode and case syntax,examples with detail information in oracle

Note: Decode and Case are very similar in their appearance but can produce very different results.
Demo Tables & Data
DECODE (overload 1)
standard.DECODE(expr NUMBER, pat NUMBER, res NUMBERRETURNNUMBER;
DECODE (overload 2)
standard.DECODE(
expr NUMBER,
pat  NUMBER,
res  VARCHAR2 CHARACTER SET ANY_CS)
return VARCHAR2 CHARACTER SET res%CHARSET;
DECODE (overload 3)
standard.DECODE(expr NUMBER, pat NUMBER, res DATERETURN DATE;
DECODE (overload 4)
standard.DECODE(
expr VARCHAR2 CHARACTER SET ANY_CS,
pat  VARCHAR2 CHARACTER SET expr%CHARSET,
res  NUMBER)
RETURN NUMBER;
DECODE (overload 5)
standard.DECODE(
expr VARCHAR2 CHARACTER SET ANY_CS,
pat  VARCHAR2 CHARACTER SET expr%CHARSET,
res  VARCHAR2 CHARACTER SET ANY_CS)
RETURN VARCHAR2 CHARACTER SET res%CHARSET;
DECODE (overload 6)
standard.DECODE(
expr VARCHAR2 CHARACTER SET ANY_CS,
pat  VARCHAR2 CHARACTER SET expr%CHARSET,
res  DATE)
RETURN DATE;
DECODE (overload 7)
standard.DECODE(expr DATE, pat DATE, res NUMBERRETURN NUMBER;
DECODE (overload 8)
standard.DECODE(
expr DATE,
pat  DATE,
res  VARCHAR2 CHARACTER SET ANY_CS)
RETURN VARCHAR2 CHARACTER SET res%CHARSET;
DECODE (overload 9)
standard.DECODE( expr DATE, pat DATE, res DATERETURN DATE;
DECODE (overload 10)
standard.DECODE(expr OBJECT, pat  OBJECT, res OBJECTRETURNOBJECT;
DECODE (overload 11)
standard.DECODE(expr UNDEFINED, pat UNDEFINED, res UNDEFINED)
RETURN UNDEFINED;

Simple DECODE
SELECT DECODE (value, <if this value>, <return this value>)
FROM DUAL;
SELECT program_id, 
  DECODE
(customer_id, 'AAL', 'American Airlines') AIRLINE,
  delivered_date
FROM airplanes
WHERE ROWNUM < 11;

More Complex DECODE
SELECT DECODE (value,<if this value>,<return this value>,
                     < if this value>,<return this value>,
                      ....)
FROM DUAL;
SELECT program_id,
       DECODE(customer_id,
              'AAL', 'American Airlines'
,
              'ILC', 'Intl. Leasing Corp.',
              'NWO', 'Northwest Orient',
              'SAL', 'Southwest Airlines',
              'SWA', 'Sweptwing Airlines',
              'USAF', 'U.S. Air Force') AIRLINE,
       delivered_date
FROM airplanes
WHERE ROWNUM < 11;

DECODE with DEFAULT
SELECT DECODE (value,<if this value>,<return this value>,
                     <if this value>,<return this value>,
                     ....
                     <otherwise this value>)
FROM DUAL;
SELECT program_id,
       DECODE(customer_id,
             'AAL', 'American Airlines',
             'ILC', 'Intl. Leasing Corp.',
             'NWO', 'Northwest Orient',
             'SAL', 'Southwest Airlines',
             'SWA', 'Sweptwing Airlines',
             'USAF', 'United States Air Force',
             'Not Known') AIRLINE,
       delivered_date
FROM airplanes
WHERE ROWNUM < 11;
Note: The following crosstabulation is the standard for 10g or earlier. In 11g use the PIVOT and UNPIVOT operators
Simple DECODE Crosstab

Note how each decode only looks at a single possible value and turns it into a new column
SELECT program_id,
       DECODE(customer_id, 'AAL', 'AAL') AMERICAN,
       DECODE(customer_id, 'DAL', 'DAL') DELTA,
       DECODE(customer_id, 'NWO', 'NWO') NORTHWEST,
       DECODE(customer_id, 'ILC', 'ILC') INTL_LEASING
FROM airplanes
WHERE ROWNUM < 20;

DECODE as an in-line view with crosstab summation
The above DECODE, in blue, used as an in-line view
SELECT program_id,
       COUNT (AMERICAN) AAL,
       COUNT (DELTA) DAL,
       COUNT (NORTHWEST) NWO,
       COUNT(INTL_LEASING) ILC
FROM (
   SELECT program_id,
          DECODE(customer_id, 'AAL', 'AAL') AMERICAN,
          DECODE(customer_id, 'DAL', 'DAL') DELTA,
          DECODE(customer_id, 'NWO', 'NWO') NORTHWEST,
          DECODE(customer_id, 'ILC', 'ILC') INTL_LEASING
   FROM airplanes)
GROUP BY program_id;

Query for DECODE demo
CREATE TABLE stores (
store_name      VARCHAR2(20),
region_dir      NUMBER(5),
region_mgr      NUMBER(5),
store_mgr1      NUMBER(5),
store_mgr2      NUMBER(5),
asst_storemgr1  NUMBER(5),
asst_storemgr2  NUMBER(5),
asst_storemgr3  NUMBER(5));

INSERT INTO stores
VALUES ('San Francisco',100,200,301,302,401,0,403);

INSERT INTO stores
VALUES ('Oakland',100,200,301,0,404,0,0);

INSERT INTO stores
VALUES ('Palo Alto',100,200,0,305,0,405,406);

INSERT INTO stores
VALUES ('Santa Clara',100,250,0,306,0,0,407);
COMMIT;

SELECT DECODE(asst_storemgr1, 0,
       
DECODE(asst_storemgr2, 0,
       
DECODE(asst_storemgr3, 0, 0, asst_storemgr3),
       asst_storemgr2), asst_storemgr1)
 ASST_MANAGER,
       DECODE(store_mgr1,0, DECODE(store_mgr2,0, 0, store_mgr2)
,
       store_mgr1)
 STORE_MANAGER,
       REGION_MGR,
       REGION_DIR
FROM stores;

DECODE with Summary Function
SELECT SUM(CA_COUNT) CA, SUM(TX_COUNT) TX
FROM (
   SELECT state,
   DECODE(state, 'CA', COUNT(*), 0) CA_COUNT,
   DECODE(state, 'TX', COUNT(*), 0) TX_COUNT
   FROM locations
   GROUP BY state);

DECODE in the WHEREclause
set serveroutput on

DECLARE
 posn  PLS_INTEGER := 0;
 empid PLS_INTEGER := 178;
 x     NUMBER;
BEGIN
  SELECT NVL(SUM(ah.quantity * ah.saleprice * ap.payoutpct), 0)
  INTO x
  FROM accessoryhistory ah, payoutpercentage ap,
  sku s, store st
  WHERE empid = DECODE(posn,
                          0, st.areadir,
                          1, st.areamgr,
                          2, NVL(st.storemgr1, st.storemgr2),
                          3, NVL(st.asstmgr1, NVL(st.asstmgr2,
                       st.asstmgr3)))
  AND ah.statustype IN ('ACT', 'DEA')
  AND ah.store = st.store
  AND s.dbid = ah.dbid
  AND s.sku = ah.sku
  AND ap.productgroup = s.productgroup
  AND ap.position = posn;

  dbms_output.put_line(x);
END;
/

DECODE Altered  WHERE Clause

Thanks to HJL
CREATE TABLE test (
pubdate  DATE,
compdate DATE,
valuecol NUMBER(5));

INSERT INTO test VALUES (TRUNC(SYSDATE), TRUNC(SYSDATE+300), 1);
INSERT INTO test VALUES (TRUNC(SYSDATE-300), TRUNC(SYSDATE), 9);
COMMIT;

SELECT * FROM test;

CREATE OR REPLACE PROCEDURE testproc (
StartDate DATE, EndDate DATE, DateType IN VARCHAR2) IS

 i PLS_INTEGER;
BEGIN
  SELECT valuecol
  INTO i
  FROM test
  WHERE DECODE(DateType, 'AA',compdate, 'BB', pubdate, compdate) <= EndDate
  AND DECODE(DateType, 'AA', compdate, 'BB', pubdate, compdate) >= StartDate;

  dbms_output.put_line(TO_CHAR(i));
END testproc;
/

set serveroutput on

exec testproc(TRUNC(SYSDATE), TRUNC(SYSDATE), 'BB');

Simple CASE Demo
SELECT CASE WHEN (<column_value>= <value>) THEN
            WHEN (<column_value> = <value>) THEN
            ELSE <value>
FROM <table_name>;
SELECT line_number,
  CASE WHEN (line_number = 1) THEN 'One'
       WHEN 
(line_number = 2) THEN 'Two'
       ELSE 
'More Than Two'
  END
 AS RESULTSET
FROM airplanes;

More Complex CASE Demo With Between
SELECT CASE WHEN (<column_value> BETWEEN <value>
             AND <value>) THEN
            WHEN (<column_value>
            BETWEEN <value> AND <value>) THEN
            ELSE <value>
FROM <table_name>;
SELECT line_number,
  CASE WHEN (line_number BETWEEN 1 AND 10) THEN 'One'
       WHEN (line_number BETWEEN 11 AND 100) THEN 'Big'
  ELSE 'Bigger'
  END
FROM airplanes;

More Complex CASE Demo With Booleans
SELECT CASE WHEN (<column_value> <= <value>) THEN
            WHEN (<column_value> <= <value>) THEN
            ELSE <value>
FROM <table_name>;
SELECT line_number,
  CASE WHEN (line_number < 10) THEN 'Ones'
       WHEN (line_number < 100) THEN 'Tens'
       WHEN (line_number < 1000) THEN 'Hundreds'
  ELSE 'Thousands'
  END RESULT_SET
FROM airplanes;

The above demo turned into a view
CREATE OR REPLACE VIEW line_number_view AS
SELECT line_number,
  CASE WHEN (line_number < 10) THEN 'Ones'
       WHEN (line_number < 100) THEN 'Tens'
       WHEN (line_number < 1000) THEN 'Hundreds'
  ELSE 'Thousands'
  END RESULT_SET
FROM airplanes;

CASE with BOOLEANS
set serveroutput on

DECLARE
 boolvar BOOLEAN := TRUE;
BEGIN
  dbms_output.put_line(CASE boolvar WHEN TRUE THEN 'TRUE' WHENFALSE THEN
  'FALSE' END);
END;
/
CASE - DECODE Comparison

The same functionality written using both functions
SELECT parameter, DECODE(SIGN(parameter-1000),-1,'C','P') AS BAND
FROM parameter_table;

SELECT parameter,
       CASE WHEN parameter < 1000 THEN 'C' ELSE 'P' END AS BAND
FROM parameter_table;

Another example using SIGN
SELECT min_cached, COUNT(*), ROUND(AVG(executions),2)
FROM (
  SELECT DECODE(min_cached,
                         0, '1) 00-01 min',
                         1, '2) 01-02min',
                         2, '2) 01-02min',
         DECODE(SIGN(min_cached -6), -1, '3) 03-05min',
         DECODE(SIGN(min_cached -16), -1, '4) 06-15min',
         DECODE(SIGN(min_cached -31), -1, '5) 16-30min',
         DECODE(SIGN(min_cached -61), -1, '6) 31-60min',
         DECODE(SIGN(min_cached-121), -1, '7) 1-2hrs',
                                          '8) 2 hrs+ ')))))) min_cached,
  executions
  FROM (
    SELECT ROUND((SYSDATE -
    TO_DATE(first_load_time,'YYYY-MM-DD/HH24:MI:SS'))*24*60) min_cached,
    executions
    FROM gv$sqlarea
    WHERE parsing_user_id != 0)
    )
GROUP BY min_cached