Top Banner
Page 1 of 16 Generating XML Output for a PL/SQL Procedure This document gives us an idea about generating XML output from a sample PL/SQL Procedure. Wehavetookanexampleprocedure‘Schedule_a’ whichgivesthedetailsof dealers present for a contract and the details of the products that are associated with them. Procedure Schedule_a has got seven cursors in it, out of which, first cursor retrieves the details of dealers and the remaining ones get the details of the products that a dealer has got. Each cursor retrieves the details of a particular category of products. So each of the loops associated with these cursors are terminating ones inside the loop, which retrieves dealer details. This Procedure also takes certain input parameters like Fiscal Year, Division, region, Sales Reps, Customer Name basing on which we will be retrieving the dealer names. Now that we know the output variables given out by the procedure, we will create a table with the columns, which can hold the values of the output variables. We need to create a table dynamically to eliminate the problem of existence of the table basing on the instance at which we run our procedure. Xml output is generated using a built-in package ‘DBMS_XMLGEN.GETXML’ which takes a single select query as an input and generates XML output for the data given out by the query. Modifications made to the procedure are: 1. Added code to create a table at run-time. 2. Code has been added in each of the inner loops to insert the details of the products into the table that we have created. 3. At the end of the main loop, we will call the built-inpackage ‘dbms_xmlgen.getxml’ by passing the select query, which retrieves the data from the table that we have created. The output of this is stored into a variable whose data type is CLOB.
16
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript

Generating XML Output for a PL/SQL ProcedureThis document gives us an idea about generating XML output from a sample PL/SQL Procedure. Weh v to a ea l poe uec e uea whc gv sted ti o a e o k n xmpe rcd r Sh d l i ie h ea s f _ h l dealers present for a contract and the details of the products that are associated with them. Procedure Schedule_a has got seven cursors in it, out of which, first cursor retrieves the details of dealers and the remaining ones get the details of the products that a dealer has got. Each cursor retrieves the details of a particular category of products. So each of the loops associated with these cursors are terminating ones inside the loop, which retrieves dealer details. This Procedure also takes certain input parameters like Fiscal Year, Division, region, Sales Reps, Customer Name basing on which we will be retrieving the dealer names. Now that we know the output variables given out by the procedure, we will create a table with the columns, which can hold the values of the output variables. We need to create a table dynamically to eliminate the problem of existence of the table basing on the instance at which we run our procedure. Xml output is generated using a built-in package MS X GE GE X DB _ ML N. T ML which takes a single select query as an input and generates XML output for the data given out by the query. Modifications made to the procedure are: 1. Added code to create a table at run-time. 2. Code has been added in each of the inner loops to insert the details of the products into the table that we have created. 3. At the end of the main loop, we will call the built-i p ca eb _mle .ex y n ak g d msx gngtmlb passing the select query, which retrieves the data from the table that we have created. The output of this is stored into a variable whose data type is CLOB.

Page 1 of 16

4. We will also include code, which is required to retrieve the data from a CLOB variable. We need to use another built-in package MS L .eL n t this. DB _ OBg te gh for 5. After we get the output and write it to an fnd_file, we will drop the table that we have created. Creating table dynamically: Code associated with creating a table at run time uses EXECUTE IMMEDIATE command. Sample code for creating a table in this way is:EXECUTE IMMEDIATE ('CREATE TABLE TEST_XML (DEALER_ID VARCHAR2(2000), DEALER_NAME VARCHAR2(240), FISCAL_YEAR VARCHAR2(2000), PRODUCT_NAME VARCHAR2(2000),

PRODUCT_DESCRIPTION VARCHAR2(2000))' ); Inserting data into the table created at run time: In order to insert data into the table created at run time, we need to declare a variable first and then use EXECUTE IMMEDIATE to populate the table with the required data. Sample code for this is:CREATE OR REPLACE PROCEDURE XML_PROC AS stmt VARCHAR2 (200); lv_val BEGIN EXECUTE IMMEDIATE ('create table TEST_XML (x number,z number)'); stmt := 'insert into TEST_XML values(:1,:2)'; EXECUTE IMMEDIATE stmt USING lv_val, lv_val1; END; NUMBER := 10; := 20;

lv_val1 NUMBER

This code results in insertion of data from a dynamically created table using an itr daev r bemt whc h stecmma d t b eeue i i Heete:: neme i t ai l s ; i a h o a t h n o e xc td n t r h 12 . , represent temporary variables into which we will be passing the values of our variables

Page 2 of 16

_ n _ s gE eueI l 1 a d l 2 ui xc t mme i ecmma d A tr h v eeue ti peeo v v n da o t n . f we a e xc t hs i e d c f code, we can see that the table y1 will be getting populated with a row of data. Generating XML output using DBMS_XMLGEN.GETXML: Now that we have created the table and inserted data into it, we can generate XML output for the data present in this table using this built-in package. In order that we view the XML output given out by this we need to declare a variable of CLOB type which could store large amount of data. Sample code associated with the usage of this code is:DECLARE lv_1 CLOB; BEGIN SELECT DBMS_XMLGEN.getxml ('SELECT * FROM TEST_XML ') INTO lv_1 FROM DUAL; END;

Retrieving data from a CLOB variable: To retrieve data from a CLOB variable we need to declare four variables: 1. len : which calculates the length of the CLOB variable using DBMS_LOB.getLength(). 2. xmlstr : which is used to store the xml output of size 32767 bytes each time when it is in the loop. 3. offset : which is a variable initialized to 32767 to restrict the amount of output fetched to 32767 for each loop. 4. retrieved: this variable is initialized to zero while declaring it and is incremented by a value of 32767 each time it displays the output. This vari lwi h l te a e fl t a e l od h v l o n a b l u e the end of the loop. The following code gives a clear idea as of the usage of variables mention above:CREATE OR REPLACE PROCEDURE USER_TEST_XML ( errbuf OUT VARCHAR2, retcode OUT VARCHAR2 ) IS lv_1 len xmlstr CLOB; NUMBER (10); VARCHAR2 (32767);

Page 3 of 16

offset

NUMBER (10)

:= 32767; := 0;

retrieved NUMBER (10) BEGIN

SELECT DBMS_XMLGEN.getxml ('SELECT * FROM TEST_XML') INTO lv_1 FROM DUAL; len := DBMS_LOB.getlength (lv_1); LOOP EXIT WHEN len = retrieved; IF (len - retrieved) < 32767 THEN SELECT SUBSTR (lv_1, retrieved + 1) INTO xmlstr FROM DUAL; retrieved := len; fnd_file.put_line (fnd_file.output, xmlstr); ELSE SELECT SUBSTR (lv_1, retrieved + 1, offset) INTO xmlstr FROM DUAL; retrieved := retrieved + offset; fnd_file.put_line (fnd_file.output, xmlstr); END IF; END LOOP; END;

In this procedure after declaring the variables we will start the begin block which will first retrieve the XML output into a clob variable lv_1.After the select statement, we have got the length of the clob variable. We have included a loop there, which will end when the retrieved data is equal to the length of the data. An IF-THEN-ELSE statement is also included inside the loop. In the IF condition we will check when the length of the data is less than 32767 bytes and if it is so, we will print the output into fnd_file and will equate the retrieved value to the len value. If the condition is not satisfied, the execution will now navigate to ELSE part and will select the first 32767 bytes of data. The Else part also increments the value of the variable te e y377 No wewi pittnothe fnd_file and will enter into the second r r v d b 26. w, ei l r iit l n loop now. Again, it will check for the length and prints the value into fnd_file. This loop will end when all the data is displayed.

Page 4 of 16

Dropping the dynamic table that we have created: As we have got all the output that we need to display into the fnd_file, we can now drop the table that we have created using EXECUTE IMMEDIATE. Sample code to drop a table created at run-time is as follows:EXECUTE IMMEDIATE ('drop table TEST_XML ');

Now , if we run the concurrent program which uses the procedure that we have modified, we will get the required XML output. Sample Procedure to get XML from a PL/SQL procedure: CREATE OR REPLACE PROCEDURE user_riso_schedule_test_x2 ( errbuf OUT VARCHAR2, retcode OUT NUMBER, p_fiscalyear IN NUMBER, p_division IN VARCHAR2 DEFAULT NULL, p_region IN VARCHAR2 DEFAULT NULL, p_salesrep IN NUMBER DEFAULT NULL, p_dealerid IN NUMBER DEFAULT NULL, p_requestid IN NUMBER DEFAULT NULL ) AUTHID CURRENT_USER IS len NUMBER (10); xmlstr VARCHAR2 (32767); offset NUMBER (10) := 32767; retrieved NUMBER (10) := 0; stmt VARCHAR2 (2000); RESULT CLOB; v_user_id VARCHAR2 (20); v_error_message VARCHAR2 (240); v_responsibility_name fnd_responsibility_tl.responsibility_name%TYPE; v_application_id fnd_responsibility_tl.application_id%TYPE; v_responsibility_id fnd_responsibility_tl.responsibility_id%TYPE; v_org_id hr_organization_units.organization_id%TYPE; v_dlrid riso_dlr_quota_headers.dealer_id%TYPE; v_executive VARCHAR2 (55); v_title VARCHAR2 (40); v_division VARCHAR2 (40);

Page 5 of 16

v_dlrname ra_addresses.address1%TYPE; v_flexvalueid fnd_flex_values.flex_value_id%TYPE; v_flexvalue fnd_flex_values.flex_value%TYPE; v_terr_line_id riso_dlr_terr_lines_view.terr_line_id%TYPE; v_filename VARCHAR2 (20); v_price_list_id NUMBER (15); v_end VARCHAR2 (15) := 'End of Dealer'; v_count_rec NUMBER; e_price_list_id EXCEPTION; e_customername EXCEPTION; e_param_null EXCEPTION; e_two_param EXCEPTION; CURSOR c_dealers IS SELECT UPPER (ra.address1) dealer_name, UPPER (rt.segment1) division, rvh.customer_id FROM ra_addresses ra, riso_dlr_terr_headers_view rvh, ra_site_uses rsu, ra_territories rt, ra_customers rc WHERE rvh.fiscal_year = p_fiscalyear AND ra.customer_id = rvh.customer_id AND ra.address_id = rsu.address_id AND rc.customer_id = rvh.customer_id AND rsu.site_use_code = 'CONT' AND rc.status = 'A' AND rsu.status = 'A' AND rsu.territory_id = rt.territory_id AND rvh.customer_id IN (SELECT v_customer_id FROM riso_schedule_temp_table) ORDER BY dealer_name; vc_dealers c_dealers%ROWTYPE;

--RISO Printer Duplicator Products CURSOR c_products_dup IS SELECT qplh.description, qplh.attribute6,

Page 6 of 16

TO_NUMBER (qplh.attribute7) FROM qp_list_headers_v qplh, qp_secondary_price_lists_v qps WHERE qplh.list_header_id = qps.list_header_id AND qps.parent_price_list_id = TO_CHAR (v_price_list_id) AND qps.description NOT IN ('NON-STOCK PARTS', 'DROPSHIP') AND qps.description NOT LIKE 'LEXMARK%'AND ( qplh.attribute6 IN ('1M', '5S') OR (qplh.attribute6 = '7O' AND TO_NUMBER (qplh.attribute7) = 150 ) ) ORDER BY 2, 3; vc_products_dup c_products_dup%ROWTYPE;

--RISO Printer Duplicator Accessory/Peripheral Products CURSOR c_products_acc IS SELECT qplh.description, qplh.attribute6, TO_NUMBER (qplh.attribute7) FROM qp_list_headers_v qplh, qp_secondary_price_lists_v qps WHERE qplh.list_header_id = qps.list_header_id AND qps.parent_price_list_id = TO_CHAR (v_price_list_id) AND qps.description NOT IN ('NON-STOCK PARTS', 'DROPSHIP') AND qps.description NOT LIKE 'LEXMARK%' AND qplh.attribute6 IN ('3A') ORDER BY 2, 3; vc_products_acc c_products_acc%ROWTYPE; BEGIN DELETE FROM riso_schedule_temp_table; EXECUTE IMMEDIATE ('CREATE TABLE USER_RISO_SCH_TEST2(DEALER_ID VARCHAR2(2000),DEALER_NAME VARCHAR2(240),FISCAL_YESR VARCHAR2(2000), PRODUCT_NAME VARCHAR2(2000),PRODUCT_DESCRIPTION VARCHAR2(2000))' ); COMMIT; --Ensure at least one of the following parameters is filled in by user. IF p_division IS NULL

Page 7 of 16

AND p_region IS NULL AND p_salesrep IS NULL AND p_dealerid IS NULL THEN RAISE e_param_null; END IF; --Ensure ONLY one of the following parameters is filled in by user. IF (p_division IS NOT NULL AND p_region IS NOT NULL) OR (p_division IS NOT NULL AND p_salesrep IS NOT NULL) OR (p_division IS NOT NULL AND p_dealerid IS NOT NULL) OR (p_region IS NOT NULL AND p_salesrep IS NOT NULL) OR (p_region IS NOT NULL AND p_dealerid IS NOT NULL) OR (p_salesrep IS NOT NULL AND p_dealerid IS NOT NULL) THEN RAISE e_two_param; END IF; --Run the riso_dynamicquery_p procedure and pass in whichever parameter --was selected by user, i.e., division, region, salesrep or dealerid. IF (p_division IS NOT NULL) THEN riso_dynamicquery_p (1, p_fiscalyear, p_division, NULL, NULL, NULL); ELSIF (p_region IS NOT NULL) THEN riso_dynamicquery_p (2, p_fiscalyear, NULL, p_region, NULL, NULL); ELSIF p_salesrep IS NOT NULL THEN riso_dynamicquery_p (3, p_fiscalyear, NULL, NULL, p_salesrep, NULL); ELSIF p_dealerid IS NOT NULL THEN riso_dynamicquery_p (4, p_fiscalyear, NULL, NULL, NULL, p_dealerid); END IF; --Open the dealer cursor and get the name and division of this dealer FOR vc_dealers IN c_dealers LOOP v_dlrid := vc_dealers.customer_id; BEGIN SELECT UPPER (ra.address1), UPPER (rt.segment1)

Page 8 of 16

INTO v_dlrname, v_division FROM ra_addresses ra, riso_dlr_quota_headers_view rqh, ra_site_uses rsu, ra_territories rt WHERE ra.customer_id = rqh.dealer_id AND rqh.dealer_id = v_dlrid AND rqh.fiscal_year = p_fiscalyear AND ra.address_id = rsu.address_id AND rsu.site_use_code = 'CONT' AND rsu.status = 'A' AND rsu.territory_id = rt.territory_id; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE e_customername; END; --Set the executive name and title of person to sign the Schedule v_executive := 'RICHARD M. MYTNIK'; v_title := 'VICE PRESIDENT AND GENERAL MANAGER'; v_division := 'NORTH AMERICAN DEALER SALES DIVISION'; DBMS_OUTPUT.put_line ( 'Dealer: ' || v_dlrid || CHR (10) || v_dlrname || CHR (10) || 'Fiscal_Year: ' || p_fiscalyear ); DBMS_OUTPUT.put_line ( 'Executive : ' || v_executive || CHR (10) || 'Title: ' || v_title || CHR (10) || 'Division: ' || v_division || ' DIVISION' );

Page 9 of 16

BEGIN SELECT price_list_id INTO v_price_list_id FROM ar_customers_v WHERE customer_id = v_dlrid; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE e_price_list_id; END; DBMS_OUTPUT.put_line ('PRINTER DUPLICATOR MACHINES, SUPPLIES, PARTS:'); v_count_rec := 0; --Open the duplicator products cursor to retrieve the products for this dealer's price list FOR vc_products_dup IN c_products_dup LOOP v_count_rec := v_count_rec + 1; DBMS_OUTPUT.put_line ( 'D Product:' || CHR (10) || UPPER (vc_products_dup.description) ); stmt := 'INSERT INTO USER_RISO_SCH_TEST2 VALUES(:1,:2,:3,:4,:5)'; EXECUTE IMMEDIATE stmt USING TO_CHAR (v_dlrid), v_dlrname, TO_CHAR (p_fiscalyear), 'PRINTER DUPLICATOR MACHINES, SUPPLIES, PARTS', vc_products_dup.description; END LOOP; IF v_count_rec = 0 THEN NULL; END IF; DBMS_OUTPUT.put_line ('PRINTER DUPLICATOR ACCESSORY/PERIPHERAL PRODUCTS:'); v_count_rec := 0; --Open the accessories products cursor to retrieve the products for this dealer's price list FOR vc_products_acc IN c_products_acc

Page 10 of 16

LOOP v_count_rec := v_count_rec + 1; DBMS_OUTPUT.put_line ( 'A Product:' || CHR (10) || UPPER (vc_products_acc.description) ); stmt := 'INSERT INTO USER_RISO_SCH_TEST2 VALUES(:1,:2,:3,:4,:5)'; EXECUTE IMMEDIATE stmt USING TO_CHAR (v_dlrid), v_dlrname, TO_CHAR (p_fiscalyear), RNT RD P I A O A C S O Y P RP R L P I E U LC T R C E S R / E IHE A PRODUCTS:A Product', vc_products_acc.description; END LOOP; IF v_count_rec = 0 THEN stmt := 'INSERT INTO USER_RISO_SCH_TEST2 VALUES(:1,:2,:3,:4,:5)'; EXECUTE IMMEDIATE stmt USING TO_CHAR (v_dlrid), v_dlrname, TO_CHAR (p_fiscalyear), 'PRINTER DUPLICATOR ACCESSORY/PERIPHERAL PRODUCTS:A Product', 'N/A'; END IF; DBMS_OUTPUT.put_line ('RISO SOLUTIONS PRODUCTS:'); v_count_rec := 0; END LOOP; SELECT DBMS_XMLGEN.getxml ('SELECT * FROM USER_RISO_SCH_TEST2') INTO RESULT FROM DUAL; len := DBMS_LOB.getlength (RESULT); LOOP

Page 11 of 16

EXIT WHEN len = retrieved; IF (len - retrieved) < 32767 THEN SELECT SUBSTR (RESULT, retrieved + 1) INTO xmlstr FROM DUAL; retrieved := len; fnd_file.put_line (fnd_file.output, xmlstr); ELSE SELECT SUBSTR (RESULT, retrieved + 1, offset) INTO xmlstr FROM DUAL; retrieved := retrieved + offset; fnd_file.put_line (fnd_file.output, xmlstr); END IF; END LOOP; EXECUTE IMMEDIATE ('drop table USER_RISO_SCH_TEST2'); EXCEPTION WHEN e_customername THEN raise_application_error (-20005, 'No customer found.'); WHEN e_price_list_id THEN raise_application_error (-20010, 'No Price List ID for this customer.'); WHEN e_param_null THEN raise_application_error (-20015, 'Must enter at least one of: Division, Region, Sales Rep, or Customer.'); WHEN e_two_param THEN raise_application_error (-20020, 'Must enter ONLY one of: Division, Region, Sales Rep, or Customer.'); END;

Page 12 of 16

Registering the PL/SQL Package: Creating Executable

Page 13 of 16

Defining the Program

Assign the Program to corresponding responsibility to run.

Page 14 of 16

Sample Code to get XML which simulates an RDF: The following code uses a Single select query to retrieve all the data that a Standard Invoice R p rwi gv o t ln wi te ru igtg. ue h ky r e ot l ie u ao g t h go pn a sWe s te ewod CURSOR ee o l h hr t group the data. CREATE OR REPLACE PROCEDURE xml_cursor_eg ( errbuf OUT VARCHAR2, retcode OUT VARCHAR2 ) AS RESULT LONG; BEGIN SELECT DBMS_XMLGEN.getxml ('SELECT rcta.trx_number, rcta.trx_date, CURSOR (SELECT hl.address1, hl.address2, hl.postal_code, hl.country FROM hz_party_sites hps, hz_locations hl WHERE hps.party_site_id = rcta.ship_to_site_use_id AND hps.location_id = hl.location_id ) "SHIP_TO_DETAILS", CURSOR (SELECT hl.address1, hl.address2, hl.postal_code, hl.country FROM hz_party_sites hps, hz_locations hl WHERE hps.party_site_id = rcta.bill_to_site_use_id AND hps.location_id = hl.location_id ) "BILL_TO_DETAILS", CURSOR (SELECT hl.address1, hl.address2, hl.postal_code, hl.country FROM hz_party_sites hps, hz_locations hl WHERE hps.party_site_id = rcta.remit_to_address_id AND hps.location_id = hl.location_id ) "REMIT_TO_DETAILS", CURSOR (SELECT rctla.customer_trx_line_id,

Page 15 of 16

rctla.line_number, rctla.quantity_ordered, rctla.quantity_invoiced, rctla.description, rctla.unit_selling_price, rctla.uom_code, rctla.quantity_invoiced * rctla.unit_selling_price "LINE_TOTAL" FROM ra_customer_trx_lines_all rctla WHERE rcta.customer_trx_id = rctla.customer_trx_id AND rctla.line_type = ''LINE'' ORDER BY rctla.line_number ) "LINE_DETAILS", CURSOR (SELECT SUM (rctla.quantity_invoiced * rctla.unit_selling_price) "INVOIVE_AMT" FROM ra_customer_trx_lines_all rctla WHERE rcta.customer_trx_id = rctla.customer_trx_id AND rctla.line_type = ''LINE'' ) "INVOICE_AMT_TOTAL", CURSOR (SELECT SUM (nvl(rctla.extended_amount,0)) "TAX_AMT" FROM ra_customer_trx_lines_all rctla WHERE rcta.customer_trx_id = rctla.customer_trx_id AND rctla.line_type = ''TAX'' ) "TAX_AMT_TOTAL", CURSOR (SELECT SUM (DECODE (line_type,''LINE'', (NVL (rctla.quantity_invoiced, 0)* NVL (rctla.unit_selling_price,0)),0)) + SUM (DECODE (line_type,''TAX'', NVL (rctla.extended_amount, 0),0)) "TOTAL" FROM ra_customer_trx_lines_all rctla WHERE rcta.customer_trx_id = rctla.customer_trx_id ) "TOTAL_AMOUNT" FROM ra_customer_trx_all rcta WHERE rcta.customer_trx_id between 4115 and 4119') INTO RESULT FROM DUAL; fnd_file.put_line (fnd_file.output, RESULT); END; /

Page 16 of 16