Oracle XMLtable giving cross joined data - sql

I have written a Oracle XML sql and it is giving output like below. I have given the whole code below from creating table to sql below for your quick help.
output: (WRONG)
ID, NAME
1 name1
1 name2
2 name1
2 name2
Want to make output like below (Required Output):
ID, NAME
1 name1
2 name2
Code:
CREATE TABLE XML_TBL
( "INSTANCE_DETAIL_XML" "SYS"."XMLTYPE"
);
SET DEFINE OFF;
Insert into XML_TBL (INSTANCE_DETAIL_XML) values ('<?xml version="1.0" encoding="UTF-8" standalone=''yes''?>
<driXML>
<sDet>
<cols>
<col>
<id>1</id>
<name>name1</name>
</col>
<col>
<id>2</id>
<name>name2</name>
</col>
</cols>
</sDet>
</driXML>
');
I tried the below sql, you may modify it or create a new one using it:
Select XT_ID.id
, XT_NAME.name
FROM xml_tbl XT
join XMLTABLE
('/driXML' PASSING XT.INSTANCE_DETAIL_XML COLUMNS
id_XML XMLType PATH 'sDet/cols/col/id'
, name_XML XMLType PATH 'sDet/cols/col/name'
) RI_XML on 1=1
join XMLTABLE('/id' PASSING RI_XML.id_XML COLUMNS ID number PATH '.') XT_ID on 1=1
join XMLTABLE('/name' PASSING RI_XML.name_XML COLUMNS NAME varchar2(50) PATH '.') XT_NAME on 1=1
;

You can just directly extract the id and name in the first XMLTABLE and if you descend through the hierarchy so that the path is '/driXML/sDet/cols/col' then each row from the XMLTYPE will be one col element and the id and name will correlate to that.
Oracle Setup:
CREATE TABLE XML_TBL( INSTANCE_DETAIL_XML ) AS
SELECT XMLTYPE( '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<driXML>
<sDet>
<cols>
<col>
<id>1</id>
<name>name1</name>
</col>
<col>
<id>2</id>
<name>name2</name>
</col>
</cols>
</sDet>
</driXML>' ) FROM DUAL;
Query:
SELECT id,
name
FROM xml_tbl XT
CROSS JOIN XMLTABLE(
'/driXML/sDet/cols/col'
PASSING XT.INSTANCE_DETAIL_XML
COLUMNS
id NUMBER PATH 'id',
name VARCHAR2(50) PATH 'name'
);
Output:
ID | NAME
-: | :----
1 | name1
2 | name2
db<>fiddle here

Related

PL/SQL extract XML namespace value

I have the XML file with this ROOT tag
<DEFeatureDataset xsi:type='typens:DEFeatureDataset'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:typens='http://www.esri.com/schemas/ArcGIS/10.1'>
How can I extract the 3rd namespace value "http://www.esri.com/schemas/ArcGIS/10.1" in a select statement using xml query ?
From How to select namespace value via XPath:
SELECT XMLQUERY(
'/DEFeatureDataset/namespace-uri-for-prefix("typens",.)'
PASSING XMLTYPE( xml ) RETURNING CONTENT
) AS typens
FROM table_name;
or
SELECT x.*
FROM table_name t
OUTER APPLY XMLTABLE(
'/DEFeatureDataset'
PASSING XMLTYPE( t.xml )
COLUMNS
typens VARCHAR2(100) PATH './namespace-uri-for-prefix("typens",.)'
) x
Which, for the sample data:
CREATE TABLE table_name ( xml ) AS
SELECT '<DEFeatureDataset
xsi:type="typens:DEFeatureDataset"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:typens="http://www.esri.com/schemas/ArcGIS/10.1"></DEFeatureDataset>' FROM DUAL
Both output:
| TYPENS |
| :-------------------------------------- |
| http://www.esri.com/schemas/ArcGIS/10.1 |
db<>fiddle here

how can I select XML attribute from oracle XMLTYPE column?

I create table in oracle 12c like this
CREATE TABLE Test_xml
(
xml_data SYS.XMLTYPE
)
and insert XML data like this into column xml_data
<?xml version="1.0" encoding="UTF-8"?>
<persons xmlns="http://XXXXX.XXX/schema/1.0" name="ABC">
<person>
<name1 value="AAAA" />
<name2 value="CCCC" />
<name3 value="XXXX" />
</person>
<person>
<name1 value="11111" />
<name2 value="22222" />
<name3 value="33333" />
</person>
</persons>
but when I try to select data out from table by query
SELECT xt.*
FROM Test_xml x,
XMLTABLE('/persons/person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH 'name1/#value'
) xt;
not thing out from the query,How can i select name1 from the table?
You can provide the (default, in this case) namespace as another argument to XMLTable:
SELECT xt.*
FROM Test_xml x,
XMLTABLE(
xmlnamespaces(default 'http://XXXXX.XXX/schema/1.0'),
'/persons/person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH 'name1/#value'
) xt;
| NAME1 |
| :---- |
| AAAA |
| 11111 |
db<>fiddle showing your original query, #Sayan's wildcard version (which gets nulls for some reason, even though it works with a CTE), and this default namespace version.
Just replace /persons/person to /*:persons/*:person
SELECT/*+ NO_XML_QUERY_REWRITE */ xt.*
FROM Test_xml x,
XMLTABLE('/*:persons/*:person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH '*:name1/#value'
) xt;
Full example:
with test_xml(xml_data) as (
select xmltype(q'[<?xml version="1.0" encoding="UTF-8"?>
<persons xmlns="http://XXXXX.XXX/schema/1.0" name="ABC">
<person>
<name1 value="AAAA" />
<name2 value="CCCC" />
<name3 value="XXXX" />
</person>
<person>
<name1 value="11111" />
<name2 value="22222" />
<name3 value="33333" />
</person>
</persons>]'
)
from dual
)
SELECT xt.*
FROM Test_xml x,
XMLTABLE('/*:persons/*:person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH '*:name1/#value'
) xt;
NAME1
--------------------
AAAA
11111

Oracle - XMLTABLE PATH get node ancestor

I have XML that looks like:
<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>
The only constant thing is <text> node and the fact that <uid> node could be 2 level up. Rest of nodes could have any name so I cannot use fully qualified paths.
Based of <text> node I need to find <uid> node which is the nearest or for simplicity 2 levels up in the tree.
I tried:
WITH cte("XML") AS (
SELECT '<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>'
FROM dual
)
SELECT x.*, c.*
FROM cte c,XMLTable('//text'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH '.'
--,guid VARCHAR2(40) PATH '../../uid' -- unsupported XQuery expression
--,guid VARCHAR2(40) PATH 'ancestor::node()[2]/uid'
-- unsupported XQuery expression\
) x
WHERE text IS NOT NULL;
db<>fiddle demo
I am looking for solution similar to SQL Server:
WITH cte("XML") AS (
SELECT CAST('<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>' AS XML)
)
SELECT x.value('../../uid[1]', 'VARCHAR(10)') AS uid
,s.x.value('.', 'VARCHAR(10)') AS "text"
FROM cte c
CROSS APPLY c."XML".nodes('//text') s(x)
db<>fiddle demo
You should use preceding - it will return all nodes except any ancestor.
Order of preceding collection is from the beginning to the end.
If you do this preceding::uid or more general preceding::* result will be (789, 123).
Combining everything together:
WITH cte("XML") AS (
SELECT '<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>'
FROM dual
)
SELECT x.*, c.*
FROM cte c,XMLTable('//text'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH '.'
,guid VARCHAR2(40) PATH '(preceding::uid)[last() -1]/data(.)' -- 2 -levelup
) x
WHERE text IS NOT NULL;
One working solution is the following:
SELECT x.*, c.*
FROM cte c,XMLTable('//text/../..'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH 'uid',
guid VARCHAR2(40) PATH 'sub/text'
) x
WHERE text IS NOT NULL;
Its result consists of the two columns 123 and XYZ.

How to add namespace to SQL Server generated XML output

I have the below XML output generated using a SQL Server query (added in the rextester link):
<Main>
<ID>1001</ID>
<details>
<name>John</name>
<age>12</age>
</details>
</Main>
I want to know how to add a namespace xmlns:json="http://www.samplenamespace.com/json" to the Main node.
Desired output:
<Main xmlns:json="http://www.samplenamespace.com/json">
<ID>1001</ID>
<details>
<name>John</name>
<age>12</age>
</details>
</Main>
Rextester link: http://rextester.com/OQZH6668
Any help!?
You need to use the WITH XMLNAMESPACES clause, for example:
---Fake tables in Stored procedure1
create table #Cdetails(cid int, name varchar(5), age int)
insert into #Cdetails
values(1001,'John',12),
(1002,'Rick',19),
(1003,'Diane',25),
(1004,'Kippy',26)
--Output of Stored procedure
create table #final(xml_data xml)
insert into #final
select
XML_data =
(select ID = cd1.cid,
details =
(
select cd.name,
cd.age
from #Cdetails cd
where cd.cid = cd1.cid
For XML Path(''), Type)
from #Cdetails cd1
For XML Path('Main'));
WITH XMLNAMESPACES ('http://www.samplenamespace.com/json' as json)
select * from #final
For XML Path('Main')
drop table #Cdetails,#final
Note the extra ; that is required when using WITH statements.
Rextester link: http://rextester.com/EBLL48414

Reverse Oracle dbms_xmlgen.getxmltype() - ie can I SQL query an xmltype rowset to get back table results?

Transforming myTableWithManyColsAndRows -> myTableWithOneXmlTypeColumn as follows:
declare
result xmltype;
begin
result := dbms_xmlgen.getxmltype('select * from myTableWithManyColsAndRows')
insert into table myTableWithOneXmlTypeColumn values result;
end;
Can I do the reverse, transform: myTableWithOneXmlTypeColumn -> myTableWithManyColsAndRows ?
That is, can I somehow construct a SQL statement that would query myTableWithOneXmlTypeColumn and return the same results as 'select * from myTableWithManyColsAndRows'?
--Creating a sample table with four columns and inserting random data.
create table myTableWithManyColsAndRows(
id_ number,
col1 varchar2(10),
col2 number,
col3 date
);
insert into myTableWithManyColsAndRows
select level, dbms_random.string('l', round(dbms_random.value(5,10))), round(dbms_random.value(1,100)),
to_date(round(dbms_random.value(2455928,2457024)),'J')
from dual
connect by level <= 7;
--Query to generate XML from the table.
select dbms_xmlgen.getxmltype('select * from myTableWithManyColsAndRows') from dual;
DBMS_XMLGEN.GETXMLTYPE('SELECT*FROMMYTABLEWITHMANYCOLSANDROWS')
-----------------------------------------------------------------
<ROWSET>
<ROW>
<ID_>1</ID_>
<COL1>ajfwmcaet</COL1>
<COL2>79</COL2>
<COL3>18-DEC-2013 00:00:00</COL3>
</ROW>
<ROW>
<ID_>2</ID_>
<COL1>wagvymnl</COL1>
<COL2>13</COL2>
<COL3>08-FEB-2012 00:00:00</COL3>
</ROW>
<ROW>
<ID_>3</ID_>
<COL1>jvlrv</COL1>
<COL2>45</COL2>
<COL3>28-DEC-2014 00:00:00</COL3>
</ROW>
<ROW>
<ID_>4</ID_>
<COL1>xirqutl</COL1>
<COL2>29</COL2>
<COL3>12-NOV-2012 00:00:00</COL3>
</ROW>
<ROW>
<ID_>5</ID_>
<COL1>rtdtmselx</COL1>
<COL2>75</COL2>
<COL3>26-JAN-2012 00:00:00</COL3>
</ROW>
<ROW>
<ID_>6</ID_>
<COL1>acvvw</COL1>
<COL2>70</COL2>
<COL3>19-SEP-2014 00:00:00</COL3>
</ROW>
<ROW>
<ID_>7</ID_>
<COL1>kbcmhh</COL1>
<COL2>62</COL2>
<COL3>26-MAY-2013 00:00:00</COL3>
</ROW>
</ROWSET>
To get back the rows and columns from XML, you need to use XMLTable.
tag has multiple tags. So, first step is to extract those and map to a XMLType( xmlrow). From this, the individual columns are extracted.
with myTableWithOneXmlTypeColumn(xmlcol) as (
select dbms_xmlgen.getxmltype('select * from myTableWithManyColsAndRows') from dual
)
select cols.id_ id_, cols.col1 col1, cols.col2 col2, to_date(cols.col3,'dd-mon-yyyy hh24:mi:ss') col3
from myTableWithOneXmlTypeColumn x,
XMLTABLE('ROWSET'
PASSING x.xmlcol
COLUMNS xmlrow xmltype PATH 'ROW'
) xmlrows,
XMLTABLE('ROW'
PASSING xmlrows.xmlrow
COLUMNS id_ number PATH 'ID_',
col1 varchar2(10) PATH 'COL1',
col2 number PATH 'COL2',
col3 varchar2(20) PATH 'COL3'
) cols;
ID_ COL1 COL2 COL3
---------- ---------- ---------- --------------------------
1 ajfwmcaet 79 18-DEC-2013 00:00:00
2 wagvymnl 13 08-FEB-2012 00:00:00
3 jvlrv 45 28-DEC-2014 00:00:00
4 xirqutl 29 12-NOV-2012 00:00:00
5 rtdtmselx 75 26-JAN-2012 00:00:00
6 acvvw 70 19-SEP-2014 00:00:00
7 kbcmhh 62 26-MAY-2013 00:00:00