?
Solved

pl/sql XMLTable INVALID_NUMBER error

Posted on 2012-12-21
12
Medium Priority
?
1,166 Views
Last Modified: 2012-12-21
I'm relatively new to pl/sql and xml.

I have an sql query that uses XMLTable to shred transaction details. When I run the query I get an INVALID_NUMBER error. If I comment out the 'Amount' element the query runs, so I'm assuming the problem lies here. I have no control over the content, so can't fix it. Is there any way to alter the XMLTable to either ignore the row with the error or do a decode to substitute zero when an invalid_number is found?

SELECT
      so.tranId
      , CHARGES.*
      FROM mytable1 so,
            XMLTABLE('//Charge' PASSING xmltype(so.xmlClob)
            COLUMNS "GeneralChargeID" NUMBER PATH 'GeneralChargeID',
                  "Name" VARCHAR2(255) PATH 'Name',
                  "Status" VARCHAR2(25) PATH 'Status',
                  "Quantity" NUMBER PATH 'Quantity',
                  "Amount" NUMBER(10,2) PATH 'Amount',
                  "CreditFlag" CHAR(1) PATH 'CreditFlag'                                
                  ) as CHARGES

I tried calling this statement, but the same error was thrown.
alter session set nls_numeric_characters=". ";
0
Comment
Question by:fredjonzeTwo
  • 6
  • 5
12 Comments
 
LVL 21

Expert Comment

by:flow01
ID: 38713785
change (temporary) the number(10,2) to VARCHAR2(25)  to get results .
Inspect the results for finding the reason.
If the number of resulting rows is too large to inspect, create a function

create or replace function is_number(p_in varchar2) return varchar2
is
   n1  number(10,2);
   rsl varchar2(1);
begin
    begin
       n1 :=  to_number(p_in);
       rsl := 'Y';
    exception when others then
        rsl := 'N';
     end;
    RETURN rsl;
end;

and select rows
AND IS_NUMBER(AMOUNT) <> 'Y'
0
 

Author Comment

by:fredjonzeTwo
ID: 38713797
I already tried the VARCHAR2 route but can't spot the problem. I don't have permissions to create a function (read-only access).
0
 
LVL 21

Expert Comment

by:flow01
ID: 38713799
and the cause is really 'bad' data then you have to leave the VARCHAR2 definition and you can use the same function for the decode

DECODE(IS_NUMBER(AMOUNT),'Y',TO_NUMBER(AMOUNT),0) AMOUNT
0
Fill in the form and get your FREE NFR key NOW!

Veeam is happy to provide a FREE NFR server license to certified engineers, trainers, and bloggers.  It allows for the non‑production use of Veeam Agent for Microsoft Windows. This license is valid for five workstations and two servers.

 

Author Comment

by:fredjonzeTwo
ID: 38713810
If I can't create a function (read only rights) is there an alternate decode statement I can use to substitute the bad value with zero?
0
 
LVL 21

Expert Comment

by:flow01
ID: 38713818
I presume you can execute a pl/sql-block

DECLARE
    function is_number(p_in varchar2) return varchar2
is
   n1  number(10,2);
   rsl varchar2(1);
begin
    begin
       n1 :=  to_number(p_in);
       rsl := 'Y';
    exception when others then
        rsl := 'N';
     end;
    RETURN rsl;
end;
BEGIN
    FOR   R1 IN (SELECT .. your select) LOOP
       if IS_NUMBER(r1.amount) = 'Y' THEN
           NULL;
      else
        DBMS_OUTPUT .PUT_LINE('bad value >'   || r1.amount || '<');
                            -- and maybe some keyvalues
      END IF;
    END LOOP;
END;
0
 

Author Comment

by:fredjonzeTwo
ID: 38713828
The pl/sql needs to be inserted into an MS SQL Server SSIS data source, so I'm limited to just using a direct query.
0
 
LVL 21

Expert Comment

by:flow01
ID: 38713838
in http://www.arikaplan.com/oracle/ari021000.html 
i found an alternative:

and length(translate(trim(AMOUNT),' +-.0123456789',' ')) <> 0
0
 

Author Comment

by:fredjonzeTwo
ID: 38713898
Now I'm really confused. When I add the where clause

where length(translate(trim(AMOUNT),' +-.0123456789',' ')) <> 0

No rows are returned.
0
 
LVL 74

Accepted Solution

by:
sdstuber earned 1280 total points
ID: 38713918
try this...


SELECT so.tranid,
       "GeneralChargeID",
       "Name",
       "Status",
       "Quantity",
       CASE WHEN REGEXP_LIKE(amount_str, '[+-]?[0-9]+.?[0-9]*') THEN TO_NUMBER(amount_str) ELSE NULL END
           "Amount",
       "CreditFlag"
  FROM mytable1 so,
       XMLTABLE(
           '//Charge'
           PASSING xmltype(so.xmlclob)
           COLUMNS "GeneralChargeID" NUMBER PATH 'GeneralChargeID',
                   "Name" VARCHAR2(255) PATH 'Name',
                   "Status" VARCHAR2(25) PATH 'Status',
                   "Quantity" NUMBER PATH 'Quantity',
                   amount_str VARCHAR2(20) PATH 'Amount',
                   "CreditFlag" CHAR(1) PATH 'CreditFlag'
       )
0
 
LVL 21

Expert Comment

by:flow01
ID: 38713984
where length(translate(trim(AMOUNT),' +-.0123456789',' ')) <> 0

'+300+'  would return 0
'100.888,333.2' would return 0
Maybe the regexp  of ststuber brings easy relief
0
 

Author Comment

by:fredjonzeTwo
ID: 38714013
I ran a quick query in RazorSQL with success. I'm currently running it in SSIS to see if that will also work. It just finished with success.

While I was waiting for SSIS I did some quick experimenting to see why the length/translate clause didn't work. The culprit? Two rows that had only a minus sign with no number. Apparently Oracle sees a lone minus sign as text instead of a sign. I could simply have a clause to filter out minus...but I think you're case statement function will handle other unknown values much bettter.

I've been yanking my hair for two days trying to figure out XMLTable and INVALID_NUMBER. Thanks...and an excellent for you!
0
 

Author Closing Comment

by:fredjonzeTwo
ID: 38714015
A great suggestion that solves a number of problems with unexpected data in XMLTable.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

From implementing a password expiration date, to datatype conversions and file export options, these are some useful settings I've found in Jasper Server.
When it comes to protecting Oracle Database servers and systems, there are a ton of myths out there. Here are the most common.
This video shows how to set up a shell script to accept a positional parameter when called, pass that to a SQL script, accept the output from the statement back and then manipulate it in the Shell.
Via a live example, show how to restore a database from backup after a simulated disk failure using RMAN.
Suggested Courses

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question