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

Sunday, 25 March 2018

BULK COLLECT Vs CURSOR

Bulk Collect will be having better performance at the cost of Server RAM.

Cursor resides in our temp tablespace but as we bulk collect into some collection and this collection resides in the computer memory(RAM). So while using cursor we should consider about our temp tablespace and while using Bulk collect we should consider about server RAM.

COLLECTIONS in Oracle

Index-By Tables (Associative Arrays) -  Same as arrays except that have no upper bounds, allowing them to constantly extend. As the name implies, the collection is indexed using BINARY_INTEGER values, which do not need to be consecutive. The collection is extended by assigning values to an element using an index value that does not currently exist.

TYPE table_type IS TABLE OF NUMBER(10)
    INDEX BY BINARY_INTEGER;

Nested Table Collections - Nested table collections are an extension of the index-by tables. The main difference between the two is that nested tables can be stored in a database column but index-by tables cannot. In addition some DML operations are possible on nested tables when they are stored in the database. During creation the collection must be dense, having consecutive subscripts for the elements. Once created elements can be deleted using the DELETE method to make the collection sparse. The NEXT method overcomes the problems of traversing sparse collections.

TYPE table_type IS TABLE OF NUMBER(10);

Varray Collections - A VARRAY is similar to a nested table except you must specifiy an upper bound in the declaration. Like nested tables they can be stored in the database, but unlike nested tables individual elements cannot be deleted so they remain dense.

TYPE table_type IS VARRAY(5) OF NUMBER(10);

Assignments and Equality Tests

Same type of collections - 
  TYPE table_type IS TABLE OF NUMBER(10);
  v_tab_1  table_type;
  v_tab_2  table_type;

  v_tab_2 := v_tab_1; -- This works

  IF v_tab_1 = v_tab_2 THEN -- This works
    ....
  END IF;


Different type of collections - 
  
  TYPE table_type_1 IS TABLE OF NUMBER(10);
  TYPE table_type_2 IS TABLE OF NUMBER(10);
  v_tab_1  table_type_1;
  v_tab_2  table_type_2;

  v_tab_2 := v_tab_1;  -- This will throw error

  IF v_tab_1 = v_tab_2 THEN  -- This will throw error
    ....
  END IF;


Collection Methods:

A variety of methods exist for collections, but not all are relevant for every collection type.

EXISTS(n) - Returns TRUE if the specified element exists.
COUNT - Returns the number of elements in the collection.
LIMIT - Returns the maximum number of elements for a VARRAY, or NULL for nested tables.
FIRST - Returns the index of the first element in the collection.
LAST - Returns the index of the last element in the collection.
PRIOR(n) - Returns the index of the element prior to the specified element.
NEXT(n) - Returns the index of the next element after the specified element.
EXTEND - Appends a single null element to the collection.
EXTEND(n) - Appends n null elements to the collection.
EXTEND(n1,n2) - Appends n1 copies of the n2th element to the collection.
TRIM - Removes a single element from the end of the collection.
TRIM(n) - Removes n elements from the end of the collection.
DELETE - Removes all elements from the collection.
DELETE(n) - Removes element n from the collection.
DELETE(n1,n2) - Removes all elements from n1 to n2 from the collection.

MULTISET Operations

  TYPE t_tab IS TABLE OF NUMBER;
  l_tab1 t_tab := t_tab(1,2,3,4,5,6);
  l_tab2 t_tab := t_tab(5,6,7,8,9,10);
  ...

MULTISET UNION {ALL | DISTINCT} Operator - 

  l_tab1 := l_tab1 MULTISET UNION l_tab2;

  l_tab1 := l_tab1 MULTISET UNION DISTINCT l_tab2;

MULTISET EXCEPT {DISTINCT} Operator

  l_tab1 := l_tab1 MULTISET EXCEPT l_tab2;

MULTISET INTERSECT {DISTINCT} Operator

  l_tab1 := l_tab1 MULTISET INTERSECT l_tab2;

https://oracle-base.com/articles/8i/collections-8i
https://docs.oracle.com/cloud/latest/db112/LNPLS/composites.htm#LNPLS005

Sunday, 29 October 2017

Query to convert Column data into a Row

The following query is very useful for converting the data in a single column into a row so the all the values can be extracted from a table into variables of different name. Using cursor it is difficult to get all the values from single column into variables of different name. 

Example of sample data available - 



Required format into which data needs to be converted -




Query which can be used for above purpose - 


SELECT  MAX(DECODE(level,1,regexp_substr(str,'[^,]+',1,level))) AS val1 ,
 MAX(DECODE(level,2,regexp_substr(str,'[^,]+',1,level))) AS val2 ,
 MAX(DECODE(level,3,regexp_substr(str,'[^,]+',1,level))) AS val3 ,
 MAX(DECODE(Level,4,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val4 ,
 MAX(DECODE(level,5,regexp_substr(str,'[^,]+',1,level))) AS val5 ,
 MAX(DECODE(level,6,regexp_substr(str,'[^,]+',1,level))) AS val6 ,
 MAX(DECODE(level,7,regexp_substr(str,'[^,]+',1,level))) AS val7 ,
 MAX(DECODE(level,8,regexp_substr(str,'[^,]+',1,level))) AS val8 ,
 MAX(DECODE(Level,9,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val9 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val10 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val11 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val12 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val13 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val14 ,
 MAX(DECODE(Level,10,Regexp_Substr(Str,'[^,]+',1,Level))) AS Val15
   INTO l_value_id1 ,
 l_value_id2 ,
 l_value_id3 ,
 l_value_id4 ,
 l_value_id5 ,
 l_value_id6 ,
 l_value_id7 ,
 l_value_id8 ,
 l_value_id9 ,
 l_value_id10 ,
 l_value_id11 ,
 l_value_id12 ,
 l_value_id13 ,
 l_Value_Id14 ,
 l_value_id15
   FROM (
         SELECT Listagg (column_to_convert,',') Within Group (Order By column_to_convert) Str
    FROM Table_Name
   WHERE 1 = 1
   GROUP BY group_by_column
   ) Tmp

   CONNECT BY regexp_substr(str,'[^,]+',1,level) IS NOT NULL;

Friday, 10 June 2016

Oracle - Global Temporary tables

To create a table named test with column col1 type varchar2 length 10, col2 type number. col3 type clob we can use CREATE TABLE statement as,

CREATE TABLE TEST(col1 VARCHAR2(10), col2 NUMBER, col3 CLOB);

Now if I insert data into the table the data is visible and accessible to all users. In many cases it is needed the data inside a table will be reside temporarily. In that case we can use temporary tables. Temporary tables are useful in applications where a result set is to be buffered. To create temporary table we have to issue CREATE GLOBAL TEMPORARY clause.

Temporary table can be of two types based on ON COMMIT clause settings.
1)ON COMMIT DELETE ROWS specifies temporary table would be transaction specific. Data persist within table up to transaction ending time. If you end the transaction the database truncates the table (delete all rows). Suppose if you issue commit or run ddl then data inside the temporary table will be lost. It is by default option.
Example:
(i)This statement creates a temporary table that is transaction specific:
CREATE GLOBAL TEMPORARY TABLE test_temp(col1 number, col2 number) ON COMMIT DELETE ROWS;
Table created.

(ii)Insert row in to the temporary table.
insert into test_temp values(3,7);
1 row created.

(iii)Look at the data in the table.
select * from test_temp;
COL1 COL2
---------- ----------
3 7

(iv)Issue Commit.
commit;
Commit complete.

(v)Now look at the data in the temporary table. As I created transaction specific temporary table(on commit delete rows) so data is lost after commit.
SQL> select * from test_temp;
no rows selected

2)ON COMMIT PRESERVE ROWS specifies temporary table would be session specific. Data persist within table up to session ending time. If you end the session the database truncates the table (delete all rows). Suppose you type exit in SQL*Plus then data inside the temporary table will be lost.
Example of Session Specific Temporary Tables:

1)Create Session Specific Temporary Table test_temp2.
CREATE GLOBAL TEMPORARY TABLE test_temp2 (col1 number, col2 number)
ON COMMIT PRESERVE ROWS;

(ii)Insert data into it and look at data both before commit and after commit.
insert into test_temp2 values(3,7);
1 row created.

SQL>select * from test_temp2;
COL1 COL2
---------- ----------
3 7

(iii) commit;
Commit Complete

(iv)select * from test_temp2;
COL1 COL2
---------- ----------
3 7

(iv)End the Session.
exit;

Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options
(v)Connect in a new session and look at data again.
$ sqlplus apps/apps@vis.world
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 – Production
With the Partitioning, OLAP and Data Mining options
SQL> select * from test_temp2;
no rows selected

This is how Global Temporary Tables are used.

Feature of Temporary Table
1.Indexes can be created on temporary tables. They are also temporary and the data in the index has the same session or transaction scope as the data in the underlying table.
2.Unlike permanent tables, temporary tables and their indexes do not automatically allocate a segment when they are created. Instead, segments are allocated when the first INSERT (or CREATE TABLE AS SELECT) is performed. This means that if a SELECT, UPDATE, or DELETE is performed before the first INSERT, the table appears to be empty.
3.DDL operations (except TRUNCATE) are allowed on an existing temporary table only if no session is currently bound to that temporary table.
4.If you rollback a transaction, the data you entered is lost, although the table definition persists.
5.A transaction-specific temporary table allows only one transaction at a time. If there are several autonomous transactions in a single transaction scope, each autonomous transaction can use the table only as soon as the previous one commits.
6.Because the data in a temporary table is, by definition, temporary, backup and recovery of temporary table data is not available in the event of a system failure.
7.It is good to know about that temporary table itself is not temporary, the data within it is temporary.

Restriction of Temporary Table
1.Temporary tables cannot be partitioned, clustered, or index organized.
2.You cannot specify any foreign key constraints on temporary tables.
3.Temporary tables cannot contain columns of nested table.
4.You cannot specify the following clauses of the LOB_storage_clause: TABLESPACE, storage_clause, or logging_clause.
5.Parallel DML and parallel queries are not supported for temporary tables. Parallel hints are ignored. Specification of the parallel_clause returns an error.
6.You cannot specify the segment_attributes_clause, nested_table_col_properties, or parallel_clause.
7.Distributed transactions are not supported for temporary tables.


Sunday, 5 June 2016

OAF - Account Generator Implementation (PAAPINVW examples)

To begin with, Account Generator Workflow needs to be customized as per the business requirement. Below is an example of PAAPINVW workflow customization - 




Once Workflow is customized, below PLSQL Code is required initiate the Customized PAAPINVW Workflow and update the generated account back in database - 


SELECT fa.application_short_name,
  fifs.id_flex_code,
  fifs.id_flex_num
INTO v_application_short_name,
  v_id_flex_code,
  v_id_flex_num
FROM fnd_id_flex_segments fifs,
  fnd_application fa
WHERE fifs.application_id = 101
AND fifs.application_id   = fa.application_id
AND id_flex_code          = 'GL#'
AND enabled_flag          = 'Y';

Pass the above retrieved variables into the below initialize procedure - 

v_seq_num := FND_FLEX_WORKFLOW.INITIALIZE(v_application_short_name, v_id_flex_code, v_id_flex_num, 'PAAPINVW');

Above process internally calls 'wf_engine.CreateProcess' process as well. 
Next steps, involves initializing the variables to be passed to workflow - 

wf_engine.setitemattrtext (
        itemtype      => 'PAAPINVW' ,
        itemkey       => v_seq_num  ,
        Aname         => 'PROJECT_ID'  ,
        avalue        => v_project_id);
    
wf_engine.setitemattrtext (
        itemtype      => 'PAAPINVW' ,
        itemkey       => v_seq_num  ,
        Aname         => 'TASK_ID'  ,
        avalue        => v_task_id);
    
wf_engine.setitemattrtext (
        itemtype      => 'PAAPINVW' ,
        itemkey       => v_seq_num  ,
        Aname         => 'AWARD_ID'  ,
        avalue        => v_award_id);   
    
wf_engine.setitemattrtext (
        itemtype      => 'PAAPINVW' ,
        itemkey       => v_seq_num  ,
        Aname         => 'EXPENDITURE_TYPE',
        avalue        => v_expenditure_type);

After initializing the variables to be passed to the Workdlow, StartProcess is called to initiate the process -

wf_engine.StartProcess('PAAPINVW', v_seq_num );

Once the process is completed, account generation status and generated account is retrieved using below commands - 

v_account_status := wf_engine.getitemattrtext ('PAAPINVW', x_seq_num, 'FND_FLEX_STATUS'); 
v_account_ccid := wf_engine.getitemattrtext('PAAPINVW', x_seq_num, 'FND_FLEX_CCID'); 
v_account_segment := wf_engine.getitemattrtext('PAAPINVW', x_seq_num, 'FND_FLEX_SEGMENTS'); 
v_account_data := wf_engine.getitemattrtext('PAAPINVW', x_seq_num, 'FND_FLEX_DATA'); 
v_account_desc := wf_engine.getitemattrtext('PAAPINVW', x_seq_num, 'FND_FLEX_DESCRIPTIONS');

Below SQL statement can be used to update the generated account - 

UPDATE Ap_Invoice_Distributions_All
SET Dist_Code_Combination_Id = V_Account_Ccid
WHERE Invoice_Id             = V_Invoice_Id
AND invoice_line_number      = v_inv_line_num
AND distribution_line_number = v_distribution_line_number;

Below is the link to the details document on Account Generator.

Reference Document for Account Generator Implementation

Monday, 21 March 2016

Project Accounting - Check funds queries at Task, Top Task and Project Level

FOR TASK

  SELECT project_id,
    (SUM(budget) - SUM(actuals)) "funds"
  FROM PA_TASK_FC_RESULTS_V
  WHERE project_id     =<p_project_id>
  AND start_date       > '31-MAR-15'
  AND end_date         < '1-APR-16'
  AND budget_version_id=
    (SELECT MAX(budget_version_id)
    FROM PA_TASK_FC_RESULTS_V
    WHERE project_id=<p_project_id>
    )
  and TASK_ID=<P_TASK_ID>
  GROUP BY project_id;
  
FOR TOP TASK 

  SELECT project_id,
    (SUM(budget) - SUM(actuals)) "funds"
  FROM PA_TOP_TASK_FC_RESULTS_V
  WHERE project_id     =<p_project_id>
  AND start_date       > '31-MAR-15'
  AND end_date         < '1-APR-16'
  AND budget_version_id=
    (SELECT MAX(budget_version_id)
    FROM PA_TOP_TASK_FC_RESULTS_V
    WHERE project_id=<p_project_id>
    )
  and TOP_TASK_ID=<P_TOP_TASK_ID>
  GROUP BY project_id;
  
FOR PROJECT 

  SELECT project_id,
    (SUM(budget) - SUM(actuals)) "funds"
  FROM PA_PRJ_FC_RESULTS_V
  WHERE project_id     =<p_project_id>
  AND start_date       > '31-MAR-15'
  AND end_date         < '1-APR-16'
  AND budget_version_id=
    (SELECT MAX(budget_version_id)
    FROM PA_PRJ_FC_RESULTS_V
    WHERE project_id=<p_project_id>
    )
  GROUP BY PROJECT_ID;

Blocking Outlook Calendar through PLSQL Code

-- Following code can be used for blocking the calendar of the recipients through email 
-- sent using PLSQL Code.
-- Create the following function

CREATE OR REPLACE FUNCTION ical_event (
   p_summary         IN VARCHAR2
 , p_organizer_name  IN VARCHAR2
 , p_organizer_email IN VARCHAR2
 , p_start_date      IN DATE
 , p_end_date        IN DATE
 , p_version         IN VARCHAR2 := NULL
 , p_prodid          IN VARCHAR2 := NULL
 , p_calscale        IN VARCHAR2 := NULL
 , p_method          IN VARCHAR2 := NULL
)
   RETURN VARCHAR2 

AS  

   l_retval VARCHAR2(32767);
   l_lf     CHAR(1) := CHR(10);

BEGIN

   l_retval := ''
      || 'BEGIN:VCALENDAR' || l_lf
      || 'VERSION:' || NVL(p_version,'2.0') || l_lf
      || 'PRODID:' || NVL(p_prodid,'-//Your company name//NONSGML ICAL_EVENT//EN') || l_lf
      || 'CALSCALE:' || NVL(p_calscale,'GREGORIAN') || l_lf
      || 'METHOD:' || NVL(p_method,'REQUEST') || l_lf
      || 'BEGIN:VEVENT' || l_lf
      || 'SUMMARY:' || p_summary || l_lf
      || 'ORGANIZER;CN="' || p_organizer_name || '":MAILTO:' || p_organizer_email || l_lf
      || 'DTSTART:' || TO_CHAR(p_start_date,'YYYYMMDD') || 'T' || TO_CHAR(p_start_date,'HH24MISS') || l_lf
      || 'DTEND:' || TO_CHAR(p_end_date,'YYYYMMDD') || 'T' || TO_CHAR(p_end_date,'HH24MISS') || l_lf
      || 'DTSTAMP:' || TO_CHAR(SYSDATE,'YYYYMMDD') || 'T' || TO_CHAR(SYSDATE,'HH24MISS') || l_lf
      || 'UID:' || RAWTOHEX(SYS_GUID()) || '@yoururl.com' || l_lf
      || 'STATUS:NEEDS-ACTION' ||  l_lf
      || 'END:VEVENT' || l_lf
      || 'END:VCALENDAR';
   
   RETURN l_retval;
      
END ical_event;


--Create the following procedure (replace "yoururl" as needed): 

CREATE OR REPLACE PROCEDURE send_ical_email (
   p_from      IN VARCHAR2
 , p_to        IN VARCHAR2
 , p_subj      IN VARCHAR2
 , p_body_html IN VARCHAR2
 , p_body_ical IN VARCHAR2
)

AS

   l_connection UTL_SMTP.CONNECTION;
   l_mail_serv  VARCHAR2(50) := 'mail.yoururl.com';
   l_mail_port  PLS_INTEGER := '25';
   l_lf         CHAR(1) := CHR(10);
   l_msg_body   VARCHAR2(32767);

BEGIN
   
   l_msg_body :=
         'Content-class: urn:content-classes:calendarmessage' || l_lf
      || 'MIME-Version: 1.0' || l_lf
      || 'Content-Type: multipart/alternative;' || l_lf
      || ' boundary="----_=_NextPart"' || l_lf
      || 'Subject: ' || p_subj || l_lf 
      || 'Date: ' || TO_CHAR(SYSDATE,'DAY, DD-MON-RR HH24:MI') || l_lf
      || 'From: <' || p_from || '> ' || l_lf 
      || 'To: ' || p_to || l_lf 
      || '------_=_NextPart' || l_lf
      || 'Content-Type: text/plain;' || l_lf
      || ' charset="iso-8859-1"' || l_lf
      || 'Content-Transfer-Encoding: quoted-printable' || l_lf
      || l_lf
      || 'You must have an HTML enabled client to view this message.' || l_lf
      || l_lf
      || '------_=_NextPart' || l_lf
      || 'Content-Type: text/html;' || l_lf
      || ' charset="iso-8859-1"' || l_lf
      || 'Content-Transfer-Encoding: quoted-printable' || l_lf
      || l_lf
      || p_body_html || l_lf
      || l_lf
      || '------_=_NextPart' || l_lf
      || 'Content-class: urn:content-classes:calendarmessage' || l_lf
      || 'Content-Type: text/calendar;' || l_lf
      || '  method=REQUEST;' || l_lf
      || '  name="meeting.ics"' || l_lf
      || 'Content-Transfer-Encoding: 8bit' || l_lf
      || l_lf
      || p_body_ical || l_lf
      || l_lf
      || '------_=_NextPart--';
            
   l_connection := utl_smtp.open_connection(l_mail_serv, l_mail_port);
   utl_smtp.helo(l_connection, l_mail_serv);
   utl_smtp.mail(l_connection, p_from);
   utl_smtp.rcpt(l_connection, p_to);
   utl_smtp.data(l_connection, l_msg_body);
   utl_smtp.quit(l_connection);
   
END send_ical_email;

-- Create a page process similar to the following that fires 
-- when the submit button is pressed (this will vary depending on step 3): 

DECLARE

   l_ical_event VARCHAR2(32767);

BEGIN

   l_ical_event := ical_event(
      p_start_date      => TO_DATE(:PXX_START_DATE || :PXX_START_TIME,'DD-MON-YYYYHH:MIPM')
    , p_end_date        => TO_DATE(:PXX_END_DATE || :PXX_END_TIME,'DD-MON-YYYYHH:MIPM')
    , p_summary         => :PXX_SUBJ
    , p_organizer_name  => :PXX_USER_NAME
    , p_organizer_email => :PXX_USER_EMAIL
   );

   send_ical_email( 
      p_to        => :PXX_TO_ADDRESS
    , p_from      => :PXX_USER_EMAIL
    , p_subj      => :PXX_SUBJ
    , p_body_html => :PXX_BODY_HTML 
    , p_body_ical => l_ical_event
   );
   
END;

That should do it. Submit the page to send the request. 

Wednesday, 9 December 2015

SQL Interview Questions



Question: How will you delete duplicating rows from a base table?

Answer: 
DELETE FROM table_name A WHERE rowid > (SELECT MIN(rowid) FROM table_name B WHERE A.key_values = B.key_values);

DELETE FROM emp e WHERE ROWID NOT IN ( SELECT MIN(ROWID) FROM emp a WHERE e.empno = a.empno);

DELETE FROM EMP WHERE ROWID NOT IN (SELCT MAX(ROWID) FROM EMP GROUP BY EMPNO);


Question: Find out nth highest salary from emp table?
Answer: 
SELECT DISTINCT (A.SAL) FROM EMP A WHERE &N = (SELECT COUNT (DISTINCT (B.SAL)) FROM EMP B WHERE A.SAL<=B.SAL);

SELECT MIN(SAL) FROM (SELECT DISTINCT SAL FROM EMP ORDER BY SAL DESC) WHERE ROWNUM <=&N;

SELECT * FROM (SELECT RANK() OVER (PARTITION BY SAL ORDER BY SAL DESC NULLS LAST) RN FROM TABLENAME) WHERE RN = &N;

SELECT ENAME, SAL, DEPTNO, JOB FROM EMP WHERE SAL=(SELECT MAX(SAL) FROM EMP WHERE LEVEL = &LEVELNO CONNECT BY PRIOR SAL>SAL GROUP BY LEVEL);

SELECT ROWNUM,SAL FROM (SELECT ROWNUM,SAL FROM EMP ORDER BY SAL DESC )GROUP BY ROWNUM,SAL HAVING ROWNUM=&N;

SELECT * FROM(SELECT EMPNO,ENAME,DEPTNO,SAL,RANK() OVER(ORDER BY SAL) TOPSAL FROM EMP) WHERE TOPSAL=&NTH;

SELECT DISTINCT A.SAL FROM EMP A, (SELECT ROWNUM AS CNT, A.* FROM (SELECT DISTINCT SAL FROM EMP ORDER BY SAL DESC) A) B WHERE A.SAL = B.SAL AND B.CNT = :A;

SELECT LEVEL,MAX(SAL) FROM EMP WHERE LEVEL=&LEVELNO CONNECT BY PRIOR SAL>SAL GROUP BY LEVEL;

Question: Which datatype is used for storing graphics and images?

Answer:  BLOB or BFILE. Long raw is obsolete now.


Question: Which is more faster - IN or EXISTS?

Answer: EXISTS is more faster than IN because EXISTS returns a Boolean value whereas IN returns a value.

In many cases, EXISTS is better because it requires you to specify a join condition, which can invoke an index scan. EXISTS is faster when sub-query result is large. IN is often better if the result of sub-query are very small. But using EXISTS is better choice when sub-query result is unpredictable.