PL/SQL extract XML namespace value - sql

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

Related

Oracle XMLtable giving cross joined data

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

XML Table- Parametrized Path

I have a problem with getting positions of the specific node.
Problem is that positions doesn't have reference to the parent node (no attribute), but they are under it in xml hierarchy.
When I try to parametrize it in this way:
SELECT something....
FROM ktr_xml x,
XMLTABLE (
'/Invoices/Invoice[#ID="' || p_invoice_number || '"]' || '/InvoiceLine'
PASSING x.xml
COLUMNS line_number VARCHAR2 (100) PATH 'ID',
Product_quantity VARCHAR2 (100)
PATH 'InvoicedQuantity',
etc.... etc....
WHERE x.id = p_id;
It gives me error, that string is expected, it means I cant build dynamic path based on field of invoice (ID).
If I run this query:
FROM ktr_xml x,
XMLTABLE (
'/Invoices/Invoice/InvoiceLine'
PASSING x.xml
Its getting all the items, not only from the specific invoice.
Any ideas how I could solve it?
You could split the query into several correlated XMLTABLE statements:
Oracle Setup:
CREATE TABLE ktr_xml( id, xml ) AS
SELECT 1, XMLTYPE(
'<Invoices>
<Invoice ID="2">
<InvoiceLine>
<ID>Invoice1</ID>
<InvoicedQuantity>42</InvoicedQuantity>
</InvoiceLine>
</Invoice>
</Invoices>'
) FROM DUAL;
Query:
SELECT x1.*
FROM ktr_xml x
INNER JOIN
XMLTABLE (
'/Invoices/Invoice'
PASSING x.xml
COLUMNS
id NUMBER PATH './#ID',
xml XMLTYPE PATH '.'
) t
ON ( t.id = 2 /*p_invoice_number*/ )
CROSS JOIN
XMLTABLE (
'Invoice/InvoiceLine'
PASSING t.xml
COLUMNS
line_number VARCHAR2 (100) PATH 'ID',
Product_quantity VARCHAR2 (100) PATH 'InvoicedQuantity'
) x1
WHERE x.id = 1 /*p_id*/;
Output:
LINE_NUMBER | PRODUCT_QUANTITY
:---------- | :---------------
Invoice1 | 42
db<>fiddle here

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.

T-SQL XML Closing Tag On Empty Elements

I am trying to write a query that returns XML using t-sql. When I run a query that does not have any sibling elements the closing tags are left as is and NOT converted to self closing tags.
create table #seg
(
HouseHoldExternalId int,
Id int
)
insert into #seg
select 2534640, 265 union
select 2534644, 265 union
select 2534650, 265 union
select 2534661, 265 union
select 2534669, 265 union
select 2534684, 265 union
select 2534689, 265 union
select 2534692, 265 union
select 2534694, 265 union
select 2534698, 265
select
1 as 'Id',
(
select
HouseHoldExternalId as 'HouseHoldExternalId',
(
select null as 'Blank',
(
select Id as 'Id', 1 as 'Status',
(
select '' for xml path (''), type
)
from #seg as aa
where aa.HouseHoldExternalId = a.HouseHoldExternalId
for xml raw ('Segment'), type
)
for xml raw ('HouseHoldSegments'), type
)
from #seg as a
for xml raw ('HouseHold'), type
)
for xml raw ('retailer'), type
A snippet from the resulting xml shows the full closing tag.
<Segment Id="265" Status="1"></Segment>
When I try to adjust my query to return a result with sibling elements, the closing tags are changed to be self closing. How can I get the full closing to stay?
select
1 as 'Id',
(
select
HouseHoldExternalId as 'HouseHoldExternalId',
(
select null as 'Blank',
(
select Id as 'Id', 1 as 'Status',
(
select '' for xml path (''), type
)
from #seg as aa
where aa.HouseHoldExternalId = a.HouseHoldExternalId
for xml raw ('Segment'), type
)
for xml raw ('HouseHoldSegments'), type
)
from #seg as a
for xml raw ('HouseHold'), type
),
(
select
HouseHoldExternalId as 'HouseHoldExternalId',
(
select null as 'Blank',
(
select Id as 'Id', 1 as 'Status',
(
select '' for xml path (''), type
)
from #seg as aa
where aa.HouseHoldExternalId = a.HouseHoldExternalId
for xml raw ('Segment'), type
)
for xml raw ('HouseHoldSegments'), type
)
from #seg as a
for xml raw ('HouseHold'), type
)
for xml raw ('retailer'), type
A snippet from the resulting xml shows the self closing tag which I do not want.
<Segment Id="265" Status="1" />

How do I wrap a value in a CDATA tag when creating XML in SQL Server?

How do I wrap OldValue in a CDATA?
SELECT
1 AS Tag,
0 AS Parent,
(
SELECT PositionDescription AS 'data()'
FROM dbo.Project where ID = 32
FOR XML PATH('')
) AS 'PositionDescription!1!OldValue!CDATA'
FOR XML EXPLICIT, TYPE
Just drop the TYPE keyword. And use the raw string rather than XML in the subquery
SELECT
1 AS Tag,
0 AS Parent,
(
SELECT PositionDescription
FROM dbo.Project where ID = 32
) AS [PositionDescription!1!OldValue!CDATA]
FOR XML EXPLICIT
Sample & Output
create table Project (PositionDescription varchar(100), id int)
insert project select 'abc<test>', 32
---
<PositionDescription>
<OldValue><![CDATA[abc<test>]]></OldValue>
</PositionDescription>