Reputation: 6593
We have a table called audit1. It has a column 'changes' that contains XML as blob. The xml looks like below. The audit table basically records changes that happen to other tables via our app's UI.
<c>
<f n="VersNo" b="1" a="2"/>
<f n="LstDate" b="20160215" a="20160217"/>
<f n="FileSweepId" b="Test" a="Test1"/>
</c>
I need to create a report that lists all changes that have occurred since a given date to certain tables. Once I have the audit1 records I am interested in, I need to list all the f nodes in the report.
That is each f node in each of the relevant audit record needs to become a row in the report.
The report is generated by our app. What the app can take for generating the report is one of the following:
I can't think of a way to achieve this via a single sql query. So, I am going down the road of writing a stored proc.
The first problem I am facing is how to iterate over the f nodes in the procedure.
Secondly, I need to figure out how to return these f nodes along with other info from audit records in out_cursor.
BEGIN FOR item IN (SELECT auditno, xmltype(changes, 1) as changes, extract(xmltype(changes, 1), '/c/f') as fields from audit1 where runlistno is null and rownum < 2 ) LOOP dbms_output.put_line(item.auditno || ' ' || item.changes.getStringVal() || ' ' || item.fields.getStringVal()); -- stumped about how to iterate over the f nodes --FOR field in ('select extractvalue(object_value, '/') x FROM TABLE(XMLSequence(' + item.fields.getStringVal() + ') ') FOR field in (select f from XMLTable('for $i in / return $i' passing item.fields columns f varchar2(200) path 'f')) LOOP dbms_output.put_line(field.f); END LOOP; END LOOP; END;
The above PL/SQL at present errors with:
ORA-19114: XPST0003 - error during parsing the XQuery expression: LPX-00801: XQuery syntax error at 'i' 1 for $i in / return $i - ^ ORA-06512: at line 6
Upvotes: 0
Views: 2641
Reputation: 36107
Why don't you use simple SQL, with chained XMLTable
functions that extract reqired fields ?
Look at simple example:
CREATE TABLE audit1(
changes CLOB
);
INSERT INTO audit1 VALUES( '<c>
<f n="VersNo" b="1" a="2"/>
<f n="LstDate" b="20160215" a="20160217"/>
<f n="FileSweepId" b="Test" a="Test1"/>
</c>'
);
INSERT INTO audit1 VALUES(
'<c>
<f n="VersNo" b="22" a="32"/>
<f n="LstDate" b="20160218" a="2016020"/>
<f n="FileSweepId" b="Test 555" a="Test1234"/>
</c>'
);
commit;
and now:
SELECT rec_no, rn, n, b, a
FROM ( select rownum rec_no, a.* FROM audit1 a ),
XMLTable( '/c'
passing xmltype( changes )
columns f_fields xmltype path './f'
) x1,
XMLTable( '/f'
passing x1.f_fields
columns rn for ordinality,
n varchar2(20) path './@n',
b varchar2(20) path './@b',
a varchar2(20) path './@a'
)
REC_NO RN N B A
---------- ---------- -------------------- -------------------- --------------------
1 1 VersNo 1 2
1 2 LstDate 20160215 20160217
1 3 FileSweepId Test Test1
2 1 VersNo 22 32
2 2 LstDate 20160218 2016020
2 3 FileSweepId Test 555 Test1234
6 rows selected
Upvotes: 1
Reputation: 6593
I was able to get rid of the error by modifying the plsql to:
BEGIN
FOR item IN (SELECT auditno, xmltype(changes, 1) as changes, extract(xmltype(changes, 1), '/c/f') as fields from audit1 where runlistno is null and rownum < 2 )
LOOP
dbms_output.put_line('parsing fields from: ' || item.fields.getStringVal());
dbms_output.put_line('name||beforevalue||aftervalue');
FOR field in (select n, b, a from XMLTable('//f' passing item.fields columns n varchar2(30) path '@n', b varchar(30) path '@b', a varchar(30) path '@a' ))
LOOP
dbms_output.put_line(field.n || '|| ' || field.b || '|| ' || field.a);
END LOOP;
END LOOP;
END;
So I am now able to iterate over the fields. But not sure yet how I can return info from the fields in the sys ref cursor. Example output from above plsql:
parsing fields from: <f n="VersNo" b="1" a="2"/><f n="LstDate" b="20160215" a="20160217"/><f n="FileSweepId" b="Test" a="Test1"/>
name||beforevalue||aftervalue
VersNo|| 1|| 2
LstDate|| 20160215|| 20160217
FileSweepId|| Test|| Test1
Upvotes: 0