I'm trying to call a webservice from plsql. I have the following code (which works):
CREATE OR REPLACE PROCEDURE WEBSERVICE AS
v_xml XMLTYPE;
BEGIN
v_xml := XMLTYPE(UTL_HTTP.REQUEST(URL => 'http://dev.markitondemand.com/Api/Quote?symbol=AAPL'));
FOR x IN (SELECT
EXTRACTVALUE(VALUE(p), '/Data/Status/text()') AS title
FROM TABLE(XMLSEQUENCE(EXTRACT(v_xml, '/QuoteApiModel/Data'))) p)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
COMMIT;
END;
But this code does not work:
CREATE OR REPLACE PROCEDURE WEBSERVICE AS
v_xml XMLTYPE;
BEGIN
v_xml := XMLTYPE(UTL_HTTP.REQUEST(URL => 'http://xkcd.com/rss.xml'));
FOR x IN (SELECT
EXTRACTVALUE(VALUE(p), 'item/title/text()') AS title
FROM TABLE(XMLSEQUENCE(EXTRACT(v_xml, '/rss/channel/item'))) p)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
COMMIT;
END;
It gives me the following error:
Connecting to the database oracle.
ORA-31011: XML parsing failed
ORA-19202: Error occurred in XML processing
LPX-00007: unexpected end-of-file encountered
ORA-06512: at "SYS.XMLTYPE", line 310
ORA-06512: at "SYS.WEBSERVICE", line 4
ORA-06512: at line 2
Process exited.
Disconnecting from the database oracle.
The difference I noticed is that the xkcd rss feed has an xml header and the other one doesn't. What should I do to make this work?
You right, error occured because of presence of XML header.
Workaround is to use XMLParse(document ...) to parse XML:
select xmlparse(document UTL_HTTP.REQUEST(URL => 'http://xkcd.com/rss.xml'))
into v_xml
from dual;
Example SQLFiddle with results from xkcd.com.
Notice, that according to documentation HTTP_UTIL.Request function returns only first 2000 bytes of server response, so you can get incomplete XML with such request.
Update
More elegant way to extract values from XML is to use XMLTable() function with XQuery :
FOR x IN (
select title
from
XMLTable(
'for $i in $p/rss/channel/item/title return $i'
passing
XMLParse(document UTL_HTTP.REQUEST(URL => '...')) as "p"
columns
title varchar2(4000) path '//title'
)
)
LOOP
DBMS_OUTPUT.PUT_LINE(x.title);
END LOOP;
SQLFiddle
At least, this method allows you to create more flexible options for different sources of RSS.
Related
I want to calling oracle array package in oracle apex page process.
My package "CREATE OR REPLACE PACKAGE SBPA.DPG_SBPA_ITEM_SUB_CAT AS
/..........................................................................
Program Purpose : SBPA_ITEM_SUB_CAT_ENTRY
Process Execution Time :
Generate By : Morshed
Generate Date : 27-Feb-2020
Modifyed Date :
...................................................................../
TYPE RefCursor is REF CURSOR;
TYPE Array_Item_Sub_Id IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_ID % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Sub_Code IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_CODE % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Sub_Desc IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_SUB_DESC % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_Item_Cat_Code IS TABLE OF SBPA_ITEM_SUB_CAT.ITEM_CAT_CODE % TYPE INDEX BY BINARY_INTEGER;
TYPE Array_RowStatus IS TABLE OF VARCHAR2(5) INDEX BY BINARY_INTEGER;
PROCEDURE DPD_SBPA_ITEM_SUB_CAT (O_Status OUT NUMBER,
P_Item_Sub_Id IN Array_Item_Sub_Id,
P_Item_Sub_Code IN Array_Item_Sub_Code,
P_Item_Sub_Desc IN Array_Item_Sub_Desc,
P_Item_Cat_Code IN Array_Item_Cat_Code,
P_RowStatus IN Array_RowStatus,
P_USER VARCHAR2);
PROCEDURE DPD_SBPA_ITEM_SUB_CAT_GRID (Cur_Data OUT RefCursor);
END DPG_SBPA_ITEM_SUB_CAT;
/"
And Package body "CREATE OR REPLACE PACKAGE BODY SBPA.DPG_SBPA_ITEM_SUB_CAT AS
/..........................................................................
Program Purpose : SBPA_ITEM_SUB_CAT_ENTRY
Process Execution Time :
Generate By : Morshed
Generate Date : 27-Feb-2020
Modifyed Date :
...................................................................../
PROCEDURE DPD_SBPA_ITEM_SUB_CAT (O_Status OUT NUMBER,
P_Item_Sub_Id IN Array_Item_Sub_Id,
P_Item_Sub_Code IN Array_Item_Sub_Code,
P_Item_Sub_Desc IN Array_Item_Sub_Desc,
P_Item_Cat_Code IN Array_Item_Cat_Code,
P_RowStatus IN Array_RowStatus,
P_USER VARCHAR2) IS
V_DataType VARCHAR2(20) :='ITEM_SUBCAT_SAVE';
V_ErrDesc VARCHAR2(500);
BEGIN
/*O_Status :=1;*/
FOR I IN P_Item_Sub_Desc.FIRST..P_Item_Sub_Desc.LAST
LOOP
IF P_RowStatus(I)=1 THEN
INSERT INTO SBPA_ITEM_SUB_CAT
(ITEM_SUB_ID, ITEM_SUB_CODE, ITEM_SUB_DESC, ITEM_CAT_CODE, STATUS, CREATE_DATE, CREATE_BY) VALUES
(SBPA_ITEM_SUB_ID_SEQ.NEXTVAL,'ITMSC-'||LPAD(SBPA_ITEM_SUB_CODE_SEQ.NEXTVAL,4,'0'),P_Item_Sub_Desc(I),P_Item_Cat_Code(I),'A',SYSDATE,P_USER);
ELSIF P_RowStatus(I)=2 THEN
UPDATE SBPA_ITEM_SUB_CAT SET
ITEM_SUB_DESC=P_Item_Sub_Desc(I),
Item_Cat_Code=P_Item_Cat_Code(I),
UPDATE_BY=P_User,
UPDATE_DATE =SYSDATE
WHERE Item_Sub_Code=P_Item_Sub_Code(I);
ELSIF P_RowStatus(I)=3 THEN
DELETE FROM SBPA_ITEM_SUB_CAT
WHERE Item_Sub_Code=P_Item_Sub_Code(I);
END IF;
END LOOP;
COMMIT;
EXCEPTION WHEN OTHERS THEN ROLLBACK;
/* O_Status :=0;*/
V_ErrDesc:=SUBSTR(SQLERRM,1,500);
INSERT INTO SBPA_ERROR_LOG
(RUN_ID, DATA_TYPE, ERROR_DESC, STATUS, RUN_DATE, RUN_BY) VALUES
(SBPA_RUN_ID_SEQ.NEXTVAL,V_DataType,V_ErrDesc,'E',SYSDATE,P_User);
COMMIT;
END DPD_SBPA_ITEM_SUB_CAT;
PROCEDURE DPD_SBPA_ITEM_SUB_CAT_GRID (Cur_Data OUT RefCursor) IS
BEGIN
OPEN CUR_DATA FOR
SELECT ITEM_SUB_ID, ITEM_SUB_CODE, ITEM_SUB_DESC,S.ITEM_CAT_CODE,C.ITEM_CAT_DESC FROM SBPA_ITEM_SUB_CAT S,SBPA_ITEM_CAT C
WHERE S.ITEM_CAT_CODE=C.ITEM_CAT_CODE
ORDER BY ITEM_SUB_ID DESC;
END DPD_SBPA_ITEM_SUB_CAT_GRID;
END DPG_SBPA_ITEM_SUB_CAT;
/"
This database code. So how can i call this package in oracle apex tabular form. Please help me..
(1) Create a button, so the processing(DML) will be performed on click of the button.
(2) Button Action -> Submit Page.
(3) Create a page Process
Point -> Processing.
Tabular Form -> Select your tabular form.
Pl/SQL Code -> Begin
package_name.procedure_name(parameters);
END;
(4) When Button Pressed: Select the created button.
(5) Execution Scope: For Created and Modified rows.
I am writing a query where I need to perform a date format transformation to meet the specified requirements.
In the database which I have to search, the date format looks like the one in the example: 5y 6m 10d with spaces in between and with optional digits (10y 30d; 1m 23d; 6m are also valid) and they are always ordered (first years, then month and then days).
The format transformation should be the following:
10y 6m 10d => 100610
1y 10m 1d => 011001
6m 2d => 000602
So that the output is always a 6-digit number.
I tried writing regular expressions within REGEX_SUBSTR to isolate the tokens and then concatenate them together in the type of SELECT REGEXP_SUBSTR(text_source, '(\d+)*y') FROM database and I also tried using the REGEX_REPLACE function. Nevertheless, I am not able to perform the transformation to two digits per token without spaces, nor replace one pattern by another, I can only replace the pattern by another string.
Although I am able to output the token separation without spaces by writing the function above. I am not able to get the whole transformation. Is there any possibility of writing a RegEx and combining it with any of the PL/SQL functions in order to transform the dates stated on the list above ? I am also open to hear any other solutions not involving RegEx, I just thought it was sensible to make a proper use of them here.
Here is a simple solution in SQL.
you get the values for year, month and day e.g. with regexp_substr.
with nvl you set the value to 0 if there it is null.
lpad it with 0
with tab as(
select '10y 6m 10d' as str from dual union all
select '1y 10m 1d ' as str from dual union all
select '6m 2d ' as str from dual
)
select lpad(nvl(y,0), 2,'0') ||lpad(nvl(m,0), 2,'0')|| lpad(nvl(d,0), 2,'0')
from (
select rtrim(regexp_substr(str, '[0-9]{1,2}y', 1),'y') as y
,rtrim(regexp_substr(str, '[0-9]{1,2}m', 1),'m') as m
,rtrim(regexp_substr(str, '[0-9]{1,2}d', 1),'d') as d
from tab
)
;
LPAD(N
------
100610
011001
000602
I hope it works
declare
myDate_ varchar2(50) := REPLACE('1y 10m 81d',' ','');
year_ varchar2(50);
month_ varchar2(50);
day_ varchar2(50);
begin
if instrb(myDate_,'y',1,1)>0 then
year_ := lpad(regexp_substr(substr(myDate_,0,instrb(myDate_,'y',1,1)), '[^y]+',1 , 1),2,0);
end if;
if instrb(myDate_,'m',1,1)>0 then
month_ := lpad(regexp_substr(substr(myDate_,instrb(myDate_,'y',1,1)+1,instrb(myDate_,'m',1,1)), '[^m]+',1 , 1),2,0);
end if;
if instrb(myDate_,'d',1,1)>0 then
day_ := lpad(regexp_substr(substr(myDate_,instrb(myDate_,'m',1,1)+1,instrb(myDate_,'d',1,1)), '[^d]+',1 , 1),2,0);
end if;
dbms_output.put_line(year_||month_||day_);
end;
I am trying to display the content of a file, split by a delimiter character.
More exactly, starting from this topic, I am trying to display the result as:
bbb
aaa
qqq
ccc
but the data source to be taken from a file.
Until now, I tried:
DECLARE
l_bfile bfile;
BEGIN
l_bfile := bfilename(my_dir, my_file);
dbms_lob.fileopen(l_bfile);
FOR i IN
(SELECT TRIM(regexp_substr(TO_CHAR(l_bfile),'[^;]+',1,level) ) AS q
FROM dual
CONNECT BY regexp_substr(TO_CHAR(l_bfile),'[^;]+',1,level) IS NOT NULL
ORDER BY level
)
LOOP
dbms_output.put_line(i.q);
END LOOP;
EXCEPTION
WHEN No_Data_Found THEN
NULL;
END;
As result, I got
PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got FILE
Can anyone give me a hint, please?
Have to write this as a new answer since this is too big for a comment to #SmartDumb:
Be advised the regex of the form '[^;]+' (commonly used for parsing delimited lists) fails when NULL elements are found in the list. Please see this post for more information: https://stackoverflow.com/a/31464699/2543416
Instead please use this form of the call to regexp_substr (note I removed the second element):
SELECT TRIM(regexp_substr('bbb;;qqq;ccc','(.*?)(;|$)',1,level, null, 1) ) AS q
FROM dual
CONNECT BY regexp_substr('bbb;;qqq;ccc','(.*?)(;|$)',1,level) IS NOT NULL
ORDER BY level
It may or may not be important in this example, it depends on if the order of the element in the string has importance to you or if you need to preserve the NULL. i.e. if you need to know the second element is NULL then this will work.
P.S. Do a search for external tables and see if that is a solution you could use. That would let you query a file as if it were a table.
You could possible try this if your file contains single line (hence the question about file structure):
DECLARE
utlFileHandle UTL_FILE.FILE_TYPE;
vLine varchar2(100);
BEGIN
utlFileHande := UTL_FILE.FOPEN(my_dir, my_file, 'r');
utl_file.get_line(utlFileHande, vLine);
FOR i IN
(SELECT TRIM(regexp_substr(vLine,'[^;]+',1,level) ) AS q
FROM dual
CONNECT BY regexp_substr(vLine,'[^;]+',1,level) IS NOT NULL
ORDER BY level
)
LOOP
dbms_output.put_line(i.q);
END LOOP;
utl_file.fclose(utlFileHande);
EXCEPTION
WHEN No_Data_Found THEN
utl_file.fclose(utlFileHande);
null;
END;
Context
Some PL/SQL package created to generate XML are throwing this error :
ORA-31061: Erreur XDB : special char to escaped char conversion failed.
This error happen because some of the text selected in the xmlelement contains control characters which are not allowed.
Solution
Replace all control chars of each xmlelement with a regex :
xmlelement("foo", REGEXP_REPLACE (bar, '[[:cntrl:]]', ''))
Problem with solution
I have 8 packages of about 5k rows each where almost each row is an xmlelement.
Other potential solution
I tought I could write a regex to replace each xmlelement's value automatically, but it fails when I have xmlelement in xmlelement with subquery and sub-subquery etc.
My Question
Is there a smarter way then replacing each xmlelement's value one by one ? I was asked to do all xmlelement of each packages to prevent further bugs but I'm sure there is a better way of doing this.
Edit
For example, you can reproduce the bug with this query :
select xmlelement("foo", unistr('\0013b')) from dual;
And I would fix it using this query :
select xmlelement("foo", regexp_replace(unistr('\0013b'), '[[:cntrl:]]', '')) from dual;
I don't think this is exactly what you want, but it is possible to generate xml for your query without error using dbms_xmlgen. Here is an example:
declare
xml_output CLOB;
my_context dbms_xmlgen.ctxHandle;
begin
my_context := dbms_xmlgen.newcontext('select unistr (''\0013b'') from dual');
xml_output := dbms_xmlgen.getxml(my_context);
dbms_xmlgen.closecontext(my_context);
dbms_output.put_line(xml_output);
end;
I have this line in my query:
IF (TEST_DESC CONTAINING 'OPEN') THEN TEST_DESC = 'OPEN';
but that is not working. Firebird says Can't Prepare , because query is empty,
when I remove the ; it says Token unknown - line 8, column 10.
(.
I want to use IF ELSE to look if the TEST_DESC field contains OPEN, if it has, it will output the word OPEN in the field. The TEST_DESC field contains SCL_OPEN I only want to show the OPEN. And the other one is that the other value contains DRV_SHORT, and I want to show SHORT only.
Thanks in advance!
IF/THEN/ELSE is a PSQL construct.
For a plain SQL query, which I infer you're using from your syntax error, use a CASE statement:
...
CASE
WHEN TEST_DESC LIKE '%OPEN%'
THEN 'OPEN'
WHEN TEST_DESC LIKE '%SHORT%'
THEN 'SHORT'
ELSE 'UNKNOWN'
END
...
There is also an IIF() function with approximately the syntax you want: IIF(TEST_DESC CONTAINING 'FOO', 'FOO', 'NO FOO').
See also this Firebird FAQ entry.