Get XML element value in Oracle DBMS_XMLDOM package - oracle

I have XML file that's stored in the clob column of the table in DB.
<?xml version="1.0" encoding="UTF-8"?>
<document>
<row>
<organization>asdklfjas;kldfj;LASKJFAS</organization>
<phones>sjhdfhjaghjskfg</phones>
<persons>hkjg</persons>
</row>
</document>
I'm using DBMS_XMLDOM package for parse it.
declare
v_clob clob;
v_doc dbms_xmldom.domdocument;
begin
...
v_doc := dbms_xmldom.newdomdocument(v_clob);
v_domelement := dbms_xmldom.getdocumentelement(v_doc);
...
end;
I need simply to get value from some element, for example <persons>. How can I do it?

There is also the option to use XslProcessor functions together with XPath:
DECLARE
v_Clob CLOB;
v_Doc XmlDom.DomDocument;
v_RootNode XmlDom.DomNode;
v_Value VARCHAR2(128);
BEGIN
...
v_Doc := XmlDom.NewDomDocument(v_Clob);
v_RootNode := XmlDom.MakeNode(XmlDom.GetDocumentElement(v_Doc));
v_Value := XmlDom.GetNodeValue(
XslProcessor.SelectSingleNode(v_RootNode, '/document/row[1]/persons/text()'));
...
END;

I found what I was searching:
...
v_doc := dbms_xmldom.newdomdocument(v_clob);
v_nodelist := dbms_xmldom.getelementsbytagname(v_doc, 'persons');
v_node := dbms_xmldom.getfirstchild(dbms_xmldom.item(v_nodelist, 0));
v_person := dbms_xmldom.getnodevalue(v_node);
...

To get XML Element value you can use either DBMS_XMLDOM or XslProcessor packages.
Refer to my answer here to construct and DBMS_XMLDOM.DOMDocument from CLOB column. Then use the below method to extract element value.
FUNCTION Get_Node_Value_From_Doc(
v_Doc IN DBMS_XMLDOM.DomDocument) RETURN VARCHAR2
IS
v_Clob CLOB;
v_RootNode DBMS_XMLDOM.DomNode;
v_Value VARCHAR2(2000);
BEGIN
v_RootNode := DBMS_XMLDOM.MakeNode(XmlDom.GetDocumentElement(v_Doc));
v_Value := DBMS_XMLDOM.GetNodeValue(XslProcessor.SelectSingleNode(v_RootNode,
'/soap:Envelope/soap:Body/GetLiveAnalysisIDSResponse[1]/AnalysisIDs[1]/guid[1]/text()'
,'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns="https://www.testkid.net/"'));
RETURN v_Value;
END Get_Node_Value_From_Doc;
Providing correct namespace is important when dealing with SOAP XML messages.

Related

How to open direct CSV file using TEXT_IO in oracle forms?

I applied this code in oracle forms 10g on trigger WHEN-BUTTON-PRESSED. This code only save the file on target location.
CODE:
PROCEDURE GEN_EXCEL IS
IN_FILE TEXT_IO.FILE_TYPE;
VC_HEAD Varchar2(32000);
vc_file_path Varchar2(50) := 'C:\';
BEGIN
IN_FILE := TEXT_IO.FOPEN(vc_file_path||'Test'||'.CSV','W');
TEXT_IO.PUT_LINE(IN_FILE,'YOUR_TITLE'||chr(10));
VC_HEAD := 'header1,header2,header3,header4';
TEXT_IO.PUT_LINE(IN_FILE,VC_HEAD);
FOR C1 IN ( SELECT column1, column2, column3, column4
FROM Table_name)
LOOP
TEXT_IO.PUT_LINE(IN_FILE,C1.col1||','||C1.col2||','||C1.col3||','||C1.col4);
END LOOP;
TEXT_IO.FCLOSE(IN_FILE);
MESSAGE('Excel file has been created!');
MESSAGE('Excel file has been created!');
EXCEPTION
WHEN Others THEN
TEXT_IO.FCLOSE(IN_FILE);
MESSAGE('Error while writing file.');
MESSAGE('Error while writing file.');
END;
I want when-button-pressed then direct open CSV file from oracle forms
How to achieve this target?
You can use webutil to do this.
It has a function client_host.
Also instead of duplicating your messages you can put pause after the first message. Then you don't need the second message for a popup of the message.
If you want to read-in your .csv file I've found a suitable solution for my problem.
Maybe it helps you too if I did understand it right what you want.
Reading From File
March 19, 2009
--------------------------------------------------
Declare
vfilename varchar2(500);
in_file Client_Text_IO.File_Type;
linebuf VARCHAR2(1800);
BEGIN
vfilename := client_get_file_name('c:/temp/', File_Filter=>'Comma Dialimeted Files (*.csv)|*.csv|');
in_file := client_Text_IO.Fopen(vfilename, 'r');
GO_BLOCK('Emp');
FIRST_RECORD;
LOOP
Client_Text_IO.Get_Line(in_file, linebuf);
p_output_line(linebuf);
Client_Text_IO.New_Line;
Next_record;
END LOOP;
FIRST_RECORD;
EXCEPTION
WHEN no_data_found THEN
Client_Text_IO.Put_Line('Closing the file...');
Client_Text_IO.Fclose(in_file);
END;
-------------------------------------------------------
PROCEDURE p_output_line(p_line varchar2) IS
vLINE VARCHAR2(4000);
vVALUE VARCHAR2(1000);
vCOMMA_COUNT NUMBER;
vREPORT_DATE DATE;
BEGIN
vLINE := p_line;
vCOMMA_COUNT := LENGTH(vLINE)- LENGTH(REPLACE(vLINE,',','')); -- COUNT THE NUMBER OF COMMAS
FOR I IN 1.. vCOMMA_COUNT+1 LOOP
vVALUE := SUBSTR(vLINE,1,INSTR(vLINE,',')-1); -- IF vLINE = 123,ABC,9877 THEN VVALUE WILL BE 123
IF vVALUE IS NULL THEN
vVALUE := vLINE;
END IF;
vLINE := SUBSTR(vLINE,INSTR(vLINE,',')+1) ; -- CHANGE 123,ABC,9877 TO BE ABC,9877
IF I = 1 THEN
:DATA.BMK_NAME := vVALUE;
ELSIF I = 2 THEN
vREPORT_DATE := last_day(to_date(vVALUE,'dd-mm-yyyy'));
:DATA.REPORT_DATE := vREPORT_DATE;
ELSIF I = 3 THEN
:DATA.BMK_RETURN := to_number(vVALUE);
END IF;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
MESSAGE('Please Check the data type is appropriate on you excel file');
MESSAGE('Please Check the data type is appropriate on you excel file');
END;
-----------------------------------------------------------------------
-- notes
1- you must install webutil version 106 or later
2- make sure that you attached and compiled the webutill.pll scucessfuly

Better Package for File Compression using PL/SQL?

I have an xlsx file sample.xlsx stored in a remote directory with around 1,699 KB in Size.
I have tried two popular PL/SQL packages (UTL_COMPRESS and AS_ZIP) that compresses them into gzip and zip, rescpectively.
With the code below using AS_ZIP, I have compressed the file to 1,619 KB:
declare
g_zipped_blob blob;
l_file_name varchar2(100) := 'sample.xlsx';
l_directory varchar2(100) := 'EXT_TAB_DATA';
begin
as_zip.add1file( g_zipped_blob, l_file_name, as_zip.file2blob(l_directory, l_file_name));
as_zip.finish_zip( g_zipped_blob );
as_zip.save_zip( g_zipped_blob, l_directory, 'my2.zip' );
dbms_lob.freetemporary( g_zipped_blob );
end;
With the code below (taken from the original post) using UTL_COMPRESS, I have compressed the file to 1,618 KB:
DECLARE
in_filename VARCHAR2(100) := 'sample.xlsx';
l_directory varchar2(100) := 'EXT_TAB_DATA';
src_file BFILE;
v_content BLOB;
v_blob_len INTEGER;
v_file utl_file.file_type;
v_buffer RAW(32767);
v_amount BINARY_INTEGER := 32767;
v_pos INTEGER := 1;
BEGIN
src_file := bfilename(l_directory, in_filename);
dbms_lob.fileopen(src_file, dbms_lob.file_readonly);
v_content := utl_compress.lz_compress(src_file, 9);
v_blob_len := dbms_lob.getlength(v_content);
v_file := utl_file.fopen(l_directory,
in_filename || '.gz',
'wb');
WHILE v_pos < v_blob_len LOOP
dbms_lob.READ(v_content, v_amount, v_pos, v_buffer);
utl_file.put_raw(v_file, v_buffer, TRUE);
v_pos := v_pos + v_amount;
END LOOP;
utl_file.fclose(v_file);
EXCEPTION
WHEN OTHERS THEN
IF utl_file.is_open(v_file) THEN
utl_file.fclose(v_file);
END IF;
RAISE;
END;
Although minimal, it seems that UTL_COMPRESS has better compression in terms of file size.
I was wondering if there was some unseen advantage using the custom AS_ZIP over the Oracle-supplied UTL_COMPRESS?
Thank you.
Anton Scheffer explains why he wrote the AS_ZIP package in this blog post . It should answer your question. Basically it's to support additional zip formats.
Also it has a link to a more recent version of the package than the link in your post.
As for which one to use, my standard line is always to use the Oracle built-in functionality unless we really need the something extra from a third-party offering.
Using Oracle's standard functionality means:
Oracle Support covers us
we don't have to maintain the code
our code base is that much simpler for new joiners

Script is failing to pass XMLTYPE argument to PLSQL procedure

I have a script that is supposed to open an xml file and read the contents and process it. The plsql procedure TEST_LOAD.BOB_LOAD has been created successfully.
TEST_LOAD.BOB_LOAD looks like this:
PROCEDURE BOB_LOAD(p_uuid IN varchar2,
xml_in IN xmltype,
msg_status OUT varchar2,
xml_out OUT xmltype);
END BOB_LOAD;
/
The script below calls this procedure in the final line and seems to be the cause of the error. I am assuming that it is not recognising the variable x as XMLTYPE.
DECLARE
xml_file UTL_FILE.FILE_TYPE;
chars_read INTEGER;
xml_clob CLOB;
xamount INTEGER :=32767;
char_buffer VARCHAR2(32767);
x XMLType;
BEGIN
xml_file := utl_file.fopen('/export/hm/testpit/bob', 'test.xml', 'r', xamount);
DBMS_LOB.CREATETEMPORARY(xml_clob, true);
LOOP
BEGIN
UTL_FILE.GET_LINE(xml_file, char_buffer);
chars_read :=LENGTH(char_buffer);
DBMS_LOB.WRITEAPPEND(xml_clob, chars_read, char_buffer);
DBMS_LOB.WRITEAPPEND(xml_clob, 1, CHR(10));
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
END LOOP;
UTL_FILE.FCLOSE(xml_file);
x := XMLType.createXML(xml_clob);
TEST_LOAD.BOB_LOAD('9718fa05-9995-4f17-jk1k-763113b8f4a4', x );
END;
/
However when I run the script above I get an ORA-06550, PLS:00306 error saying:
ORA-06550: line 39, column 3:
PLS-00306: wrong number or types of arguments in call to 'BOB_LOAD'
ORA-06550: line 39, column 3:
PL/SQL: Statement ignored
Your procedure has 4 parameters. You call it with only 2. You need to call it with the out parameters as well.
Much easier way to read a file's contents into a CLOB:
function read_clob_from_file(p_directory varchar2
,p_filename varchar2) return clob is
l_amt number := dbms_lob.lobmaxsize;
l_dst_loc clob;
l_dst_offset number := 1;
l_lang_ctx number := dbms_lob.default_lang_ctx;
l_src_loc bfile;
l_src_offset number := 1;
l_warning number;
begin
l_src_loc := bfilename(p_directory, p_filename);
dbms_lob.createtemporary(l_dst_loc, true);
dbms_lob.fileopen(l_src_loc, dbms_lob.file_readonly);
dbms_lob.loadclobfromfile(l_dst_loc
,l_src_loc
,l_amt
,l_dst_offset
,l_src_offset
,dbms_lob.default_csid
,l_lang_ctx
,l_warning);
dbms_lob.fileclose(l_src_loc);
return l_dst_loc;
end;

how to manually cache the values of function calls in 10g

I have the following code
CREATE OR REPLACE FUNCTION slow_function (p_in IN NUMBER)
RETURN NUMBER
AS
BEGIN
DBMS_LOCK.sleep(1);
RETURN p_in;
END;
/
CREATE OR REPLACE PACKAGE cached_lookup_api AS
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER;
PROCEDURE clear_cache;
END cached_lookup_api;
/
CREATE OR REPLACE PACKAGE BODY cached_lookup_api AS
TYPE t_tab IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
g_tab t_tab;
g_last_use DATE := SYSDATE;
g_max_cache_age NUMBER := 10/(24*60); -- 10 minutes
-- -----------------------------------------------------------------
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER AS
l_value NUMBER;
BEGIN
IF (SYSDATE - g_last_use) > g_max_cache_age THEN
-- Older than 10 minutes. Delete cache.
g_last_use := SYSDATE;
clear_cache;
END IF;
BEGIN
l_value := g_tab(p_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Call function and cache data.
l_value := slow_function(p_id);
g_tab(p_id) := l_value;
END;
RETURN l_value;
END get_cached_value;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
PROCEDURE clear_cache AS
BEGIN
g_tab.delete;
END;
-- -----------------------------------------------------------------
END cached_lookup_api;
/
I want to pass two parameters "pi_value1" and "pi_value2" both of varchar2 to the function slow_function instead of "p_in". Is is possible to cache the results with two in parameters in oracle 10g .
the above code works fine with 1 parameter.
Please any one explain?
You'd need to create a two-dimensional array type to cache the values. Something along the lines of this (omitting the cache expiration code since that isn't changing)
CREATE OR REPLACE PACKAGE BODY cached_lookup_api
AS
TYPE t_pi_value2_tbl IS TABLE OF NUMBER
INDEX BY VARCHAR2(100);
TYPE t_cache IS TABLE OF t_pi_value2_tbl
INDEX BY VARCHAR2(100);
g_cache t_cache;
FUNCTION get_cached_value( p_pi_value1 IN VARCHAR2,
p_pi_value2 IN VARCHAR2 )
IS
BEGIN
RETURN g_cache(p_pi_value1)(p_pi_value2);
EXCEPTION
WHEN no_data_found
THEN
g_cache(p_pi_value1)(p_pi_value2) := slow_function( p_pi_value1, p_pi_value2 );
RETURN g_cache(p_pi_value1)(p_pi_value2);
END;
END;

How to loop through a delimited list in Oracle PLSQL

I am working on an Oracle procedure that calls another procedure within it. One of my parameters (parm1) can contain one or more values in a comma separated list. How can I loop through these values to pass them one at a time to another procedure?
Here is an example of what I would like it to do:
When Parm1 = 123,312
callProcedure2(123)
callProcedure2(321)
-or-
When Parm1 123
callProcedure2(123)
I think this can be accomplished using a loop but I can't figure out how to get it to use each value as a separated call within the loop.
Any help would be appreciated!
Thanks!
CURSOR V_CUR IS
select regexp_substr(Parm1 ,'[^,]+', 1, level) As str from dual
connect by regexp_substr(Parm1, '[^,]+', 1, level) is not null;
This curor will give you result like this
123
321
Now iterate the cursor and call the procedure in loop.
For i IN V_CUR
LOOP
callProdcedure2(i.str);
END LOOP;
Just loop through substrings:
declare
parm1 varchar2(1000) := '123,234,345,456,567,789,890';
vStartIdx binary_integer;
vEndIdx binary_integer;
vCurValue varchar2(1000);
begin
vStartIdx := 0;
vEndIdx := instr(parm1, ',');
while(vEndIdx > 0) loop
vCurValue := substr(parm1, vStartIdx+1, vEndIdx - vStartIdx - 1);
-- call proc here
dbms_output.put_line('->'||vCurValue||'<-');
vStartIdx := vEndIdx;
vEndIdx := instr(parm1, ',', vStartIdx + 1);
end loop;
-- Call proc here for last part (or in case of single element)
vCurValue := substr(parm1, vStartIdx+1);
dbms_output.put_line('->'||vCurValue||'<-');
end;
There is a utility procedure COMMA_TO_TABLE and array type DBMS_UTILITY.UNCL_ARRAY dedicated for this task. Since Oracle 10g.
It is well document here.
Here is a sample solution:
SET SERVEROUTPUT ON
DECLARE
csvListElm VARCHAR2(4000) := 'elm1, elm2,elm3 ,elm4 , elm5';
csvListTable DBMS_UTILITY.UNCL_ARRAY;
csvListLen BINARY_INTEGER;
currTableName VARCHAR2(222);
BEGIN
DBMS_UTILITY.COMMA_TO_TABLE(csvListElm, csvListLen, csvListTable);
FOR csvElm IN 1..(csvListTable.COUNT - 1) LOOP
dbms_output.put_line('-- CSV element : <'||csvListTable(csvElm)||'>');
dbms_output.put_line('-- Trimmed CSV element: <'||trim(csvListTable(csvElm))||'>');
END LOOP;
END;
/
Sample output:
-- CSV element : <elm1>;
-- Trimmed CSV element: <elm1>;
-- CSV element : < elm2>;
-- Trimmed CSV element: <elm2>;
-- CSV element : <elm3 >;
-- Trimmed CSV element: <elm3>;
-- CSV element : <elm4 >;
-- Trimmed CSV element: <elm4>;
-- CSV element : < elm5>;
-- Trimmed CSV element: <elm5>;
It is possible to use a function that you can use in a for loop (without regexp for ThinkJet):
Create a type and function
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION cto_table(p_sep in Varchar2, p_list IN VARCHAR2)
RETURN t_my_list
AS
l_string VARCHAR2(32767) := p_list || p_sep;
l_sep_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab t_my_list := t_my_list();
BEGIN
LOOP
l_sep_index := INSTR(l_string, p_sep, l_index);
EXIT
WHEN l_sep_index = 0;
l_tab.EXTEND;
l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index));
l_index := l_sep_index + 1;
END LOOP;
RETURN l_tab;
END cto_table;
/
Then how to call it in the for loop:
DECLARE
parm1 varchar2(4000) := '123,234,345,456,567,789,890';
BEGIN
FOR x IN (select * from (table(cto_table(',', parm1)) ) )
LOOP
dbms_output.put_line('callProdcedure2 called with ' || x.COLUMN_VALUE);
callProdcedure2(x.COLUMN_VALUE);
END LOOP;
END;
/
Notice the default name COLUMN_VALUE given by Oracle, which is necessary for the use I want to make of the result.
Result as expected:
callProdcedure2 called with 123
callProdcedure2 called with 234
...

Resources