Showing posts with label PL/SQL. Show all posts
Showing posts with label PL/SQL. Show all posts

29 Jan 2013

XML Parsing

Since Oracle 9i Release 1 (9.0.1), a new datatype, XMLType, has been introduced to facilitate native handling of XML data in the database.

The following example demonstrate how to read an XML file using the XMLType.

XML

<?xml version="1.0"?> <customer_list> <sales_person_staff_no>201201234</sales_person_staff_no> <sales_person_name>Ada Cheng</sales_person_name> <customer> <customer_code>1234</customer_code> <customer_name>ABC Trading Co., Ltd.</customer_name> <contact> <contact_name>Mickey Mouse</contact_name> <phone_no>987654321</phone_no> </contact> <contact> <contact_name>Donald Duck</contact_name> <phone_no>987654322</phone_no> </contact> </customer> <customer> <customer_code>1235</customer_code> <customer_name>Sunshine Co., Ltd.</customer_name> <contact> <contact_name>Felix the Cat</contact_name> <phone_no>987654322</phone_no> </contact> </customer> </customer_list>

PL/SQL

create or replace PROCEDURE SP_XML_PARSING ( as_if_dir IN VARCHAR2, /* Oracle Directory of the XML File */ as_if_name IN VARCHAR2 /* File Name of the XML File */ ) AS /************************************************************************ NAME: SP_XML_PARSING PURPOSE: Parse an XML file. ************************************************************************/ CURSOR c_customer_list (f_xml XMLTYPE) IS SELECT customer_list.* FROM XMLTable('/customer_list' passing f_xml COLUMNS STAFF_NO VARCHAR2(20) path 'sales_person_staff_no', STAFF_NAME VARCHAR2(50) path 'sales_person_name', CUSTOMERS XMLTYPE path 'customer' ) AS customer_list; CURSOR c_customer (f_xml XMLTYPE) IS SELECT customer.* FROM XMLTable('/customer' passing f_xml COLUMNS CUSTOMER_CD VARCHAR2(20) path 'customer_code', CUSTOMER_NAME VARCHAR2(100) path 'customer_name', CONTACTS XMLTYPE path 'contact' ) AS customer; CURSOR c_contacts (f_xml XMLTYPE) IS SELECT contacts.* FROM XMLTable('/contact' passing f_xml COLUMNS NAME VARCHAR2(50) path 'contact_name', PHONE_NO VARCHAR2(50) path 'phone_no' ) AS contacts; TYPE lt_customer_list IS TABLE OF c_customer_list%ROWTYPE INDEX BY BINARY_INTEGER; TYPE lt_customer IS TABLE OF c_customer%ROWTYPE INDEX BY BINARY_INTEGER; TYPE lt_contacts IS TABLE OF c_contacts%ROWTYPE INDEX BY BINARY_INTEGER; la_customer_list lt_customer_list; la_customer lt_customer; la_contacts lt_contacts; lx_if XMLTYPE; BEGIN lx_if := XMLTYPE(bfilename(as_if_dir, as_if_name), nls_charset_id('AL32UTF8')); -- Customer List -- OPEN c_customer_list(lx_if); LOOP FETCH c_customer_list BULK COLLECT INTO la_customer_list; FOR i IN 1..la_customer_list.COUNT LOOP dbms_output.put_line('Staff No.: ' || la_customer_list(i).STAFF_NO); dbms_output.put_line('Staff Name: ' || la_customer_list(i).STAFF_NAME); dbms_output.put_line('**********************'); -- Customers -- IF c_customer%ISOPEN THEN CLOSE c_customer; END IF; OPEN c_customer(la_customer_list(i).CUSTOMERS); LOOP FETCH c_customer BULK COLLECT INTO la_customer; FOR j IN 1..la_customer.COUNT LOOP dbms_output.put_line('Cusomter Code: ' || la_customer(j).CUSTOMER_CD); dbms_output.put_line('Cusomter Name: ' || la_customer(j).CUSTOMER_NAME); dbms_output.put_line('----------------------'); -- Contacts -- IF c_contacts%ISOPEN THEN CLOSE c_contacts; END IF; OPEN c_contacts(la_customer(j).CONTACTS); LOOP FETCH c_contacts BULK COLLECT INTO la_contacts; FOR k IN 1..la_contacts.COUNT LOOP dbms_output.put_line('Contact Name: ' || la_contacts(k).NAME); dbms_output.put_line('Phone No.: ' || la_contacts(k).PHONE_NO); dbms_output.put_line('######################'); END LOOP; EXIT WHEN c_contacts%NOTFOUND; END LOOP; -- End Contacts -- END LOOP; EXIT WHEN c_customer%NOTFOUND; END LOOP; -- End Customers -- END LOOP; EXIT WHEN c_customer_list%NOTFOUND; END LOOP; -- End Customer List -- CLOSE c_contacts; CLOSE c_customer; CLOSE c_customer_list; END;

Output

Staff No.: 201201234 Staff Name: Ada Cheng ********************** Cusomter Code: 1234 Cusomter Name: ABC Trading Co., Ltd. ---------------------- Contact Name: Mickey Mouse Phone No.: 987654321 ###################### Contact Name: Donald Duck Phone No.: 987654322 ###################### Cusomter Code: 1235 Cusomter Name: Sunshine Co., Ltd. ---------------------- Contact Name: Felix the Cat Phone No.: 987654322 ######################

References

  1. Using XMLType, http://docs.oracle.com/cd/B10500_01/appdev.920/a96620/xdb04cre.htm

28 Jan 2013

Bulk Binding: BULK COLLECT

Use BULK COLLECT to retrieve large volume of data by using a single operation to bind the DML statements to whole collections, reducing the number of context switches, and hence greatly increases the performance.

In my experience in reading an XML file with more than 20,000 records, it took more than an hour to parse the data using an ordinary FOR loop.  However, after using the BULK COLLECT, it is greatly reduced to less than 2 minutes.

create or replace PROCEDURE SP_BULK_COLLECT AS /********************************************************************* NAME: SP_BULK_COLLECT PURPOSE: Example for fetching the record in large volume. *********************************************************************/ CURSOR c_products IS SELECT PRODUCT_CD FROM TBL_PRODUCT ORDER BY PRODUCT_CD; TYPE lt_products IS TABLE OF c_products%ROWTYPE INDEX BY BINARY_INTEGER; la_products lt_products; BEGIN OPEN c_products; LOOP FETCH c_products BULK COLLECT INTO la_products LIMIT 100; FOR i IN 1..la_products.COUNT LOOP dbms_output.put_line(la_products(i).PRODUCT_CD); END LOOP; EXIT WHEN c_products%NOTFOUND; END LOOP; CLOSE c_products; END;

References

  1. Introduction to Bulking, http://www.dba-oracle.com/plsql/t_plsql_bulking.htm

24 Jan 2013

Reversing the loop: REVERSE

Demonstrate the use of REVERSE keyword in a LOOP.

create or replace PROCEDURE SP_REVERSE_LOOP AS /********************************************************************* NAME: SP_REVERSE_LOOP PURPOSE: An example to reverse a looping. *********************************************************************/ la_nums  DBMS_SQL.VARCHAR2A; BEGIN FOR i IN 1..10 LOOP la_nums(i) := i * i; END LOOP; FOR i IN 1..10 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); FOR i IN 10..1 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); FOR i IN REVERSE 1..10 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); END;

Output

1: 1 2: 4 3: 9 4: 16 5: 25 6: 36 7: 49 8: 64 9: 81 10: 100 ****************** ****************** 10: 100 9: 81 8: 64 7: 49 6: 36 5: 25 4: 16 3: 9 2: 4 1: 1 ******************

Use of Cursor

  1. Cursor
    create or replace PROCEDURE SP_CURSOR_EXAMPLE1 AS /**************************************************************** NAME: SP_CURSOR_EXAMPLE1 PURPOSE: Example for looping records in a cursor. ****************************************************************/ CURSOR c_products IS SELECT PRODUCT_CD FROM TBL_PRODUCT ORDER BY PRODUCT_CD; BEGIN FOR r_proudct IN c_products LOOP dbms_output.put_line(r_proudct.PRODUCT_CD); END LOOP; END;

18 Jan 2013

File Handling using PL/SQL

An Oracle directory is a database object pointing to a operating system directory on the database server machine for reading and writing files.

  1. Create Oracle Directory.
    CREATE OR REPLACE DIRECTORY scott_dir as '/home/scott';
  2. Grant Privileges.
    GRANT read, write ON DIRECTORY scott_dir TO scott;
  3. Read file.
    create or replace PROCEDURE SP_READ_FILE ( as_file_dir IN VARCHAR2, /* Oracle Directory */ as_file_name IN VARCHAR2 /* File Name */ ) AS lh_file_handle      UTL_FILE.FILE_TYPE; ls_line             VARCHAR2(1000); BEGIN lh_file_handle := UTL_FILE.FOPEN(as_file_dir, as_file_name, 'R'); LOOP UTL_FILE.GET_LINE(lh_file_handle,ls_line); dbms_output.put_line(ls_line); END LOOP; UTL_FILE.FCLOSE(lh_file_handle); END;
  4. Write file.
    CREATE OR REPLACE PROCEDURE SP_WRITE_TO_FILE ( as_file_dir VARCHAR2, /* Oracle Directory of the Output Text */ as_file_name VARCHAR2 /* File Name of the Output Text */ ) AS lh_file_handle    UTL_FILE.FILE_TYPE; BEGIN lh_file_handle := UTL_FILE.FOPEN(as_file_dir, as_file_name, 'W', 32000); UTL_FILE.PUT_LINE(lh_file_handle, 'Hello World!!!'); UTL_FILE.FCLOSE(lh_file_handle); EXCEPTION WHEN UTL_FILE.INVALID_OPERATION THEN dbms_output.put_line('Invalid operation Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_FILEHANDLE THEN dbms_output.put_line('Invalid File Handler Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.READ_ERROR THEN dbms_output.put_line('Read Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.WRITE_ERROR THEN dbms_output.put_line('Write Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_PATH THEN dbms_output.put_line('Invalid Path Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_MODE THEN dbms_output.put_line('Invalid Mode Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INTERNAL_ERROR THEN dbms_output.put_line('Internal Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN VALUE_ERROR THEN dbms_output.put_line('Value Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN OTHERS THEN dbms_output.put_line('Other errors: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); END SP_WRITE_TO_FILE;