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