generation_xml_plsql
TRANSCRIPT
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.
We have took an example procedure ‘Schedule_a’ which gives the details of
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-in package ‘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.
Page 2 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 ‘DBMS_LOB.getLength’for this.
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 NUMBER := 10;
lv_val1 NUMBER := 20;
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;
This code results in insertion of data from a dynamically created table using an
intermediate variable ‘stmt; which has the command to be executed in it. Here the :1,:2
represent temporary variables into which we will be passing the values of our variables
Page 3 of 16
‘lv_1’ and ‘lv_2’ using Execute Immediate command. After we have executed this piece of
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(<variable name>).
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 variable will hold the value of ‘len’ at
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 CLOB;
len NUMBER (10);
xmlstr VARCHAR2 (32767);
Page 4 of 16
offset NUMBER (10) := 32767;
retrieved NUMBER (10) := 0;
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
‘retrieved’ by 32767. Now, we will print it into the fnd_file and will enter into the second
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 5 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_USERIS
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 6 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_dealersIS
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_fiscalyearAND ra.customer_id = rvh.customer_idAND ra.address_id = rsu.address_idAND rc.customer_id = rvh.customer_idAND rsu.site_use_code = 'CONT'AND rc.status = 'A'
AND rsu.status = 'A' AND rsu.territory_id =rt.territory_idAND 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 ProductsCURSOR c_products_dupIS
SELECT qplh.description,qplh.attribute6,
Page 7 of 16
TO_NUMBER (qplh.attribute7)FROM qp_list_headers_v qplh, qp_secondary_price_lists_v qpsWHERE 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 ProductsCURSOR c_products_accIS
SELECT qplh.description,qplh.attribute6,TO_NUMBER (qplh.attribute7)
FROM qp_list_headers_v qplh, qp_secondary_price_lists_v qpsWHERE 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 TABLEUSER_RISO_SCH_TEST2(DEALER_ID VARCHAR2(2000),DEALER_NAMEVARCHAR2(240),FISCAL_YESR VARCHAR2(2000),
PRODUCT_NAME VARCHAR2(2000),PRODUCT_DESCRIPTIONVARCHAR2(2000))'
);COMMIT;
--Ensure at least one of the following parameters is filled in by user.IF p_division IS NULL
Page 8 of 16
AND p_region IS NULLAND p_salesrep IS NULLAND p_dealerid IS NULL
THENRAISE 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)
THENRAISE 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 NULLTHEN
riso_dynamicquery_p (3, p_fiscalyear, NULL, NULL, p_salesrep, NULL);ELSIF p_dealerid IS NOT NULLTHEN
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 dealerFOR vc_dealers IN c_dealersLOOP
v_dlrid := vc_dealers.customer_id;
BEGINSELECT UPPER (ra.address1),
UPPER (rt.segment1)
Page 9 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_idAND rqh.dealer_id = v_dlridAND rqh.fiscal_year = p_fiscalyearAND ra.address_id = rsu.address_idAND rsu.site_use_code = 'CONT'AND rsu.status = 'A'AND rsu.territory_id = rt.territory_id;
EXCEPTIONWHEN NO_DATA_FOUNDTHEN
RAISE e_customername;END;
--Set the executive name and title of person to sign the Schedulev_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 10 of 16
BEGINSELECT price_list_id INTO v_price_list_id FROM ar_customers_vWHERE customer_id = v_dlrid;
EXCEPTIONWHEN NO_DATA_FOUNDTHEN
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 listFOR vc_products_dup IN c_products_dupLOOP
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 stmtUSING 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 = 0THEN
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 listFOR vc_products_acc IN c_products_acc
Page 11 of 16
LOOPv_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 USINGTO_CHAR (v_dlrid),v_dlrname,TO_CHAR (p_fiscalyear),
‘PRINTER DUPLICATOR ACCESSORY/PERIPHERAL PRODUCTS:A Product',
vc_products_acc.description;END LOOP;IF v_count_rec = 0THENstmt := 'INSERT INTO USER_RISO_SCH_TEST2 VALUES(:1,:2,:3,:4,:5)';
EXECUTE IMMEDIATE stmt USINGTO_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 * FROMUSER_RISO_SCH_TEST2')
INTO RESULTFROM DUAL;
len := DBMS_LOB.getlength (RESULT);
LOOP
Page 12 of 16
EXIT WHEN len = retrieved;
IF (len - retrieved) < 32767THEN
SELECT SUBSTR (RESULT, retrieved + 1)INTO xmlstrFROM DUAL;
retrieved := len;fnd_file.put_line (fnd_file.output, xmlstr);
ELSESELECT SUBSTR (RESULT, retrieved + 1, offset)
INTO xmlstrFROM DUAL;
retrieved := retrieved + offset;fnd_file.put_line (fnd_file.output, xmlstr);
END IF;END LOOP;
EXECUTE IMMEDIATE ('drop table USER_RISO_SCH_TEST2');
EXCEPTIONWHEN e_customername THENraise_application_error (-20005, 'No customer found.');WHEN e_price_list_id THENraise_application_error (-20010, 'No Price List ID for this customer.');WHEN e_param_null THENraise_application_error(-20015,'Must enter at least one of: Division, Region, Sales Rep, or Customer.');WHEN e_two_param THENraise_application_error(-20020,'Must enter ONLY one of: Division, Region, Sales Rep, or Customer.');END;
Page 13 of 16
Registering the PL/SQL Package:
Creating Executable
Page 14 of 16
Defining the Program
Assign the Program to corresponding responsibility to run.
Page 15 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 InvoiceReport will give out along with the grouping tags. We use the keyword ‘CURSOR’ here to 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 hlWHERE hps.party_site_id = rcta.ship_to_site_use_idAND 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 hlWHERE hps.party_site_id = rcta.bill_to_site_use_idAND 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 hlWHERE hps.party_site_id = rcta.remit_to_address_idAND hps.location_id = hl.location_id
) "REMIT_TO_DETAILS",CURSOR (SELECT rctla.customer_trx_line_id,
Page 16 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 rctlaWHERE 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 rctlaWHERE rcta.customer_trx_id = rctla.customer_trx_idAND rctla.line_type = ''LINE''
) "INVOICE_AMT_TOTAL",CURSOR (SELECT SUM (nvl(rctla.extended_amount,0)) "TAX_AMT"
FROM ra_customer_trx_lines_all rctlaWHERE rcta.customer_trx_id = rctla.customer_trx_idAND 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 rctlaWHERE rcta.customer_trx_id = rctla.customer_trx_id
) "TOTAL_AMOUNT"FROM ra_customer_trx_all rctaWHERE rcta.customer_trx_id between 4115 and 4119')
INTO RESULTFROM DUAL;
fnd_file.put_line (fnd_file.output, RESULT);END;
/