2

I have this XML.

<a>
   <b>b1</b>
   <c>c1</c>
   <b>b2</b>
   <c>c2</c2>
</a>

I want to be able to extract the values of elements 'b' and 'c' using PL\SQL. I'm using Oracle 10g.

So far I have this,

  SELECT   XML.b                    
       , XML.c
    FROM XMLTable (
           '/a' PASSING p_xml
              COLUMNS
                 b            VARCHAR(2) PATH 'b/.'
               , c            VARCHAR(2) PATH 'c/.'
    ) XML

But I keep getting this error:

19279. 00000 -  "XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence" 
*Cause:    The XQuery sequence passed in had more than one item.
*Action:   Correct the XQuery expression to return a single item sequence.

Then I tried this:

  SELECT   XML.b                    
         , XML1.c
    FROM XMLTable (
           '/a/b' PASSING p_xml
              COLUMNS
                 b            VARCHAR(2) PATH '.'
    ) XML,
        XMLTable (
           '/a/c' PASSING p_xml
              COLUMNS
                 c            VARCHAR(2) PATH '.'
    ) XML1

But the results were:

b1,c1
b1,c2
b2,c1
b2,c2

When I want only: b1 c1 b2 c2

Can you guys help me out?

2 Answers 2

1

I didnt run it but as far as i see below code should work ;

DECLARE
vs_Xml VARCHAR2(32000):= '<INPUT>
  <A>
    <B>1</B>
  </A>
  <A>
    <B>2</B>
  </A>
</INPUT>';

vx_ParameterList   XMLTYPE;
vx_Parameter       XMLTYPE;
vn_ParameterIndex  NUMBER;
vs_Key             VARCHAR2(64);
vs_XPath           VARCHAR2(255);
vs_Value           VARCHAR2(10000);

BEGIN
vx_ParameterList := xmltype(vs_Xml);
vn_ParameterIndex := 1;
vs_XPath := '/INPUT/A'; 

WHILE vx_ParameterList.existsNode(vs_XPath || '[' || vn_ParameterIndex || ']') = 1 LOOP
  vx_Parameter := vx_ParameterList.extract(vs_XPath || '[' || vn_ParameterIndex || ']');

  vs_Value := vx_Parameter.extract('//B/text()').GetStringVal();
  vn_ParameterIndex := vn_ParameterIndex + 1;

  dbms_output.put_line(vs_Value);
END
Sign up to request clarification or add additional context in comments.

Comments

1

You have to adjust the xquery expression in the XMLTABLE command. The basic idea is to produce a stream of xml records each of which can be mapped to columns of a database record.

The obstacle in the original xml is that the xpath axis /a returns - phrased sloppily - the content of all xml records.

The solution is to build an iterator over /a that delivers the xml records one-by-one (technically: wrapped in an synthetic x element).

The following can be issued from sqlplus:

      SELECT   myxml.b
           ,   myxml.c                            
        FROM XMLTable (
'for $i in (1, let $x := fn:count(/a/b) return $x )
 let $b := /a/b[$i], $c := /a/c[$i]
 return <x>
           <b>{$b}</b>
           <c>{$c}</c>
        </x>
' PASSING XMLTYPE(
'<a>
   <b>b1</b>
   <c>c1</c>
   <b>b2</b>
   <c>c2</c>
</a>')
                  COLUMNS
                     b            VARCHAR(2) PATH '/x/b'
                   , c            VARCHAR(2) PATH '/x/c'
        ) myxml
;

Update

In order to allow for arbitrary number of b/c children, the following line

for $i in (1, let $x := fn:count(/a/b) return $x )

replaces the original

for $i in (1, 2)

in the above expression.

Reference

More info about XQuery syntax can be found in the W3c spec (deep-links to the syntax doc forthe so-called FWLOR expressions).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.