Check length of column in XMLTable - sql

This is my XML in an XMLTYPE column:
<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>
I want that if the length of a field exceeds the allowed length, 15 charcaters, then use a default value instead.
In the guide for DB2 I found this example:
select ...from .., XMLTABLE
('$INFO/customerinfo*' passing
columns
city varchar(16) path a'addr/city(if(string-lenght(.)<=16) then . else "Error!")'
) ..
How can I do it in Oracle? This is my current XMLTABLE attempt:
select ..XMLTABLE
('customers/*' passing ..
columns
...
"address" varchar(15) path 'indirizzo/(if(lenght(.)<=15) then . else "Error!")'
) data;
But that gets:
ORA-19237. unable to resolve call to function -fn:length

You have typos and inconsistent naming so it's hard to tell exactly what you're really doing to get that error, but it works if you use the string-length() function from your DB2 example, rather than length() (or lenght()):
"address" varchar2(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
With modified data to trigger the error behaviour:
select *
from XMLTABLE ('customers/*'
passing xmltype('<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc def ghi jkl mno</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>')
columns
"address" varchar(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
) data;
address
---------------
Error!
abc

Related

XML PARSING ON ORACLE (HELP :/)

I have xml data like this;
<NctsYYSResponse xmlns="http://http://www.w3.org/2001/XMLSchema-instance">
<Root>
<Response xmlns="">
<RefID>11084672</RefID>
<Guid>b2231713</Guid>
<Durum>Thank You!</Durum>
</Response>
</Root>
</NctsYYSResponse>
I tried this sql codes but i could not get result;
1- SELECT EXTRACTVALUE (XMLType(xml_respond) , '/NctsYYSResponse/Root/Response/Guid') guid from table1
2-SELECT x.GUID,x.STATUS FROM table1,XMLTABLE(
'/NctsYYSResponse/Root/Response'
PASSING XMLTYPE(xml_respond)
COLUMNS
"GUID" VARCHAR2(20) PATH 'Guid',
"STATUS" VARCHAR2(100) PATH 'Durum') x ;
3-select x.GUID from table1, xmltable(
xmlnamespaces ('http://http://www.w3.org/2001/XMLSchema-instance' as "a" ,
default ''), '/NctsYYSResponse/Root/a:Response'
passing XMLType(xml_respond)
columns
"GUID" VARCHAR2(20) path 'Guid'
) x ;
Return null at all of them
If i change NctsYYSResponse 's type xmlns to xmlns:xsi , i can get result. But i need to get result when it is xmlns . Please help me :/
You were very close with your last attempt; you just applied the a namespace to the wrong elements:
select x.guid, x.status
from table1
cross apply xmltable (
xmlnamespaces (
'http://http://www.w3.org/2001/XMLSchema-instance' as "a",
default ''
),
'/a:NctsYYSResponse/a:Root/Response'
passing XMLType(xml_respond)
columns
guid VARCHAR2(20) path 'Guid',
status VARCHAR2(100) PATH 'Durum'
) x;
GUID STATUS
-------------------- ---------------
b2231713 Thank You!
db<>fiddle

Selecting element from xml using xmlquery in Db2 (SQL/XML)

I have created table in Db2 like this:
create table xml_file(data xml not null)
This is the exact structure of xml:
<?xml version="1.0" encoding="UTF-8" ?>
<student id="20140021">
<name>Tom</name>
<surname>Johnson</surname>
<birth_date>"05/11/1995"</birth_date>
<birth_place>"Miami"</birth_place>
<points>9.45</points>
</student>
I want to select id, name, surname, and points for all students whose names are Ben and birth places are Chicago.
I wrote something like this:
select xmlquery('$DATA/student/data(#id)') as ID,
xmlquery('$DATA/student/name/text()') as NAME,
xmlquery('$DATA/student/surname/text()') as SURNAME,
xmlquery('$DATA/student/points/text()') as POINTS
from xml_file
where xmlexists('$DATA/student[birth_place = "Chicago"]')
and xmlexists('$DATA/student[name = "Ben"]');
All I got is this message: "FETCHED 0 RECORDS, 0 RECORDS SHOWN" (this is in IBM Data Studio).
Can someone tell me what did I do wrong?
Try this:
/*
WITH xml_file (data) AS
(
VALUES
XMLPARSE
(DOCUMENT '<?xml version="1.0" encoding="UTF-8" ?>
<student id="20140021">
<name>Tom</name>
<surname>Johnson</surname>
<birth_date>"05/11/1995"</birth_date>
<birth_place>"Miami"</birth_place>
<points>9.45</points>
</student>'
)
)
*/
SELECT X.*
FROM
xml_file V
, XMLTABLE
('$doc/student' PASSING V.data AS "doc"
COLUMNS
ID INT PATH '#id'
, NAME VARCHAR(20) PATH 'name'
, SURNAME VARCHAR(20) PATH 'surname'
, POINTS DEC(5, 2) PATH 'points'
) X
WHERE XMLEXISTS('$doc/student[birth_place = """Miami""" and name = "Tom"]' PASSING V.data AS "doc");
Try replacing your two where xmlexistss with one (this is used with your sample xml in the question, not your code):
where xmlexists('$DATA//student[birth_place/text()['Miami']][name/text()["Tom"]]');
or, two are required:
where xmlexists('$DATA/student[birth_place/text()['Miami']]')
and xmlexists('$DATA/student[name/text()["Tom"]]');
and see if either works.
The birth-place element contains double quotations marks which lead to an XPath evaluation error. To avoid this, replace where xmlexists('$DATA/student[birth_place = "Chicago"]') with one the following XPath expression :
XPath 1.0 friendly :
where xmlexists('$DATA/student/birth_place[substring(.,2,string-length(/student/birth_place)-2)="Chicago"]')
XPath 2.0 friendly :
where xmlexists('$DATA/student/birth_place[translate(.,codepoints-to-string(34),"")="Chicago"]')
XPath used to test on your sample data :
/student/birth_place[substring(.,2,string-length(/student/birth_place)-2)="Miami"]
/student/birth_place[translate(.,codepoints-to-string(34),"")="Miami"]

Parsing XML with XMLTable n Oracle

I have one xml which holds customer and order details. I am parsing it using XMLTable. I think I am giving the correct XPath but I am getting no output.
Here is what I have tried. Hoping for assistance.
**DECLARE
l_raw_xml CLOB:= '<?xml version="1.0" encoding="utf-8"?>
<Root
xmlns="http://www.adventure-works.com">
<Customers>
<Customer CustomerID="GREAL">
<CompanyName>Great Lakes Food Market</CompanyName>
<ContactName>Howard Snyder</ContactName>
<ContactTitle>Marketing Manager</ContactTitle>
<Phone>(503) 555-7555</Phone>
<FullAddress>
<Address>2732 Baker Blvd.</Address>
<City>Eugene</City>
<Region>OR</Region>
<PostalCode>97403</PostalCode>
<Country>USA</Country>
</FullAddress>
</Customer>
</Customers>
<Orders>
<Order>
<CustomerID>LETSS</CustomerID>
<EmployeeID>6</EmployeeID>
<OrderDate>1997-11-10T00:00:00</OrderDate>
<RequiredDate>1997-12-08T00:00:00</RequiredDate>
<ShipInfo ShippedDate="1997-11-21T00:00:00">
<ShipVia>2</ShipVia>
<Freight>45.97</Freight>
<ShipName>Let Stop N Shop</ShipName>
<ShipAddress>87 Polk St. Suite 5</ShipAddress>
<ShipCity>San Francisco</ShipCity>
<ShipRegion>CA</ShipRegion>
<ShipPostalCode>94117</ShipPostalCode>
<ShipCountry>USA</ShipCountry>
</ShipInfo>
</Order>
</Orders>
</Root>';
l_xml_type XMLTYPE:=XMLTYPE.createXML(l_raw_xml);
cursor c_make_xml_object
is
SELECT x.*,
y.*
FROM XMLTABLE('/Root/Customers/Customer'
PASSING l_xml_type
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'CompanyName',
FullAddress XMLTYPE PATH 'FullAddress') x,
XMLTABLE('/FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'Address',
City VARCHAR2(255) PATH 'City') y ;
BEGIN
FOR rec IN c_make_xml_object
loop
DBMS_OUTPUT.PUT_LINE(rec.CustomerID);
DBMS_OUTPUT.PUT_LINE(rec.CompanyName);
END LOOP;
END;
/**
Do I have to account for the namespace? I had added namespace code but it did not work/
Change to this to include a default namespace:
FROM XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'),
'/Root/Customers/Customer' ....
....
XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'), '/FullAddress'
.....
Yes, a proper namespace definition is needed within the SQL query such as below :
SELECT x.*, y.*
FROM XML_TAB t,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:Root/ns:Customers/ns:Customer'
PASSING t.xml_data
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'ns:CompanyName',
FullAddress XMLTYPE PATH 'ns:FullAddress'
) x,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'ns:Address',
City VARCHAR2(255) PATH 'ns:City') y ;
Demo with SQL
Demo with PL/SQL

Extracting a node where xmlns is set to blank

I'm having difficulties extracting the value from certain nodes in an XML structure using XMLTABLE. Below query works perfectly when you remove the xmlns="" attribute from the SubListItem nodes. And as you can see, the XML already has a default namespace. I honestly have no clue how I can deal with this "blanking out" of the namespace on certain nodes like this.
For further clarification, the creation of this XML is not within my control and is provided by a third-party. I've also changed the names of the nodes and the content from the delivered files while preserving the structure of the XML.
SELECT f.airline, f.flightnumber, fl.gate
FROM xmltable(
xmlnamespaces(
default 'http://some/name.space',
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
'Body/Flight'
passing xmltype(
'<?xml version="1.0" encoding="utf-16"?>
<Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://some/name.space">
<Sender>
<System>ConnectionManagement</System>
</Sender>
<Flight>
<Airline>ABC</Airline>
<Number>1234</Number>
<SubList>
<SubListItem xmlns="">
<Gate>X</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Y</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Z</Gate>
</SubListItem>
</SubList>
</Flight>
</Body>'
)
columns airline varchar2(100) path 'Airline'
, flightNumber VARCHAR2(5) path 'Number'
, subList XMLTYPE path 'SubList'
) f
, xmltable (
xmlnamespaces( default 'http://some/name.space'),
'/SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
How can I target the Gate node when the XML looks like this?
Leave the default namespace alone in the second XMLTable, and specify a named namespace for the path you do have:
...
, xmltable (
xmlnamespaces( 'http://some/name.space' as "ns"),
'/ns:SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
AIRLINE FLIGH GATE
---------- ----- -----
ABC 1234 X
ABC 1234 Y
ABC 1234 Z
The SubList still has to match that, but as the child nodes don't the default is incorrect the way you have it. If you remove the xmlns="" as you mentioned in the question then that inherits the namespace from its parent, so your default works. With that override to no-namespace you can't use a default.

Select values from XML in SQL

I need to select values from an XML document. These values are stored as childnodes as follows:
<customers>
<customer>
<kunnr>1</kunnr>
<kdgrp>2</kdgrp>
</customer>
<customer>
<kunnr>2</kunnr>
<kdgrp>2</kdgrp>
</customer>
</customers>
I need to select the values of kunnr and kdgrp for every customer node. I expect a result like this:
kunnr kdgrp
1 2
2 2
What I tried so far:
SELECT #xml.query('/customers/customer/kunnr') AS KUNNR,
#xml.query('/customers/customer/kdgrp') AS KDGRP
This results in one row with two colums containing XML:
KUNNR KDGRP
<kunnr>1</kunnr><kunnr>2</kunnr> <kdgrp>2</kdgrp><kdgrp>2</kdgrp>
Another try:
SELECT C.value('/kunnr/text()','nvarchar(10)') as KUNNR,
C.value('/kdgrp/text()','nvarchar(10)') as KDGRP
from #xml.nodes('/customers/customer') AS T(C);
This resulted in the following error message:
XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
Maybe something like this:
DECLARE #xml XML
SET #xml='<customers>
<customer>
<kunnr>1</kunnr>
<kdgrp>2</kdgrp>
</customer>
<customer>
<kunnr>2</kunnr>
<kdgrp>2</kdgrp>
</customer>
</customers>'
And then a query like this:
SELECT
c.value('kunnr[1]', 'nvarchar(10)') AS kunnr,
c.value('kdgrp[1]', 'nvarchar(10)') AS kdgrp
FROM
#xml.nodes('//customers/customer') as t(c)
This will give you this result:
kunnr kdgrp
1 2
2 2
I had a problem related to extracting values from T-SQL XML and found an issue that may help others. When retrieving data with: .value('(/root/subnode)[1]', 'varchar(max)') that call would not retrieve data but the following call did: .value('(//subnode)[1]', 'varchar(max)'). Note that the working version replaced the root node with a /. The problem with the first call seemed to be that the root node came with a specification of an xml namespace like &< root xmlns="http://www..." &> and to get the .value call to return data I needed to get past the specification of the namespace which was causing things to fail for some reason.