SQL Server Where Clause Path for XML Value - sql

Main problem is how to use a where clause in SQL for XML.
I need to return the row of XML where the "team" appears in.
You can view my example fiddle here: http://sqlfiddle.com/#!18/de221e/2
Table:
CREATE TABLE test
(
id int,
value xml
)
INSERT INTO test (id, value)
VALUES
('1', N'<?xml version="1.0" encoding="utf-16" standalone="yes"?> <Atts>
<Att>
<Name>test</Name>
</Att>
<Att>
<Name>team</Name>
</Att>
<Att>
<Name>test</Name>
</Att>
</Atts>'),
('2', N'<?xml version="1.0" encoding="utf-16" standalone="yes"?> <Atts>
<Att>
<Name>test</Name>
</Att>
<Att>
<Name>test</Name>
</Att>
<Att>
<Name>test</Name>
</Att>
</Atts>');
query:
select * from test
where value.value('(/Atts/Att/Name)[1]','varchar(max)') = 'team'
This doesn't return anything.
However if you do where clause on first name that appears in the XML it works e.g.
select * from test
where value.value('(/Atts/Att/Name)[1]','varchar(max)') = 'test'
returns:
| id | value |
|----|---------------------------------------------------------------------------------------------------|
| 1 | <Atts><Att><Name>test</Name></Att><Att><Name>team</Name></Att><Att><Name>test</Name></Att></Atts> |
| 2 | <Atts><Att><Name>test</Name></Att><Att><Name>test</Name></Att><Att><Name>test</Name></Att></Atts> |
Expected results is this query should return:
select * from test
where value.value('(/Atts/Att/Name)[1]','varchar(max)') = 'team'
| id | value |
|----|---------------------------------------------------------------------------------------------------|
| 1 | <Atts><Att><Name>test</Name></Att><Att><Name>team</Name></Att><Att><Name>test</Name></Att></Atts> |
Any ideas how I can return "team" if it appears in XML but isn't in the first

It is better to use exist() method. It will check for the 'team' value regardless of its position. exist() Method (xml Data Type)
SQL
select * from test
where value.exist('/Atts/Att/Name[./text()="team"]') = 1;

It is the second one. You are looking at the first one.
This gives you the results you want
select * from test
where value.value('(/Atts/Att/Name)[2]','varchar(max)') = 'team'

Related

Filter table by an XML column where they have equal values - SQL Server 2017

I need to filter the records of a table where the values of an XML component are equal.
TempTable
ID
noteID
columnXML
1
342342
xml1
2
235987
xml2
and the structure of the xml is the following:
<ArrayOfNoteParameterDC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NoteParameterDC>
<ParameterEnum>License</ParameterEnum>
<Value>100299</Value>
<ParameterName>License</ParameterName>
</NoteParameterDC>
<NoteParameterDC>
<ParameterEnum>Vehicle</ParameterEnum>
<Value>Unknown</Value>
<ParameterName>Vehicle</ParameterName>
</NoteParameterDC>
<NoteParameterDC>
<ParameterEnum>DestinationAccount</ParameterEnum>
<Value>543152065</Value>
<ParameterName>DestinationAccount</ParameterName>
</NoteParameterDC>
<NoteParameterDC>
<ParameterEnum>SourceAccount</ParameterEnum>
<Value>543152065</Value>
<ParameterName>SourceAccount</ParameterName>
</NoteParameterDC>
<NoteParameterDC>
<ParameterEnum>CustomerVehicleId</ParameterEnum>
<Value>27104593</Value>
<ParameterName>CustomerVehicleId</ParameterName>
</NoteParameterDC>
</ArrayOfNoteParameterDC>
My goal is to be able to filter the records that have the same number of "value" of "DestinationAccount" and the "value" of "SourceAccount"
I don't have much knowledge with XML queries, what occurs to me is to do something like this:
select * from TempTable n
where n.columnXML.value('(/ArrayOfNoteParameterDC/NoteParameterDC/Value/text())[1]', 'varchar(max)') = ...
I don't know how to differentiate the nodes "SourceAccount" and "DestinationAccount".
I appreciate any help
If I understood correctly, what do you need:
select * from TempTable
where columnXML.value('(/ArrayOfNoteParameterDC/NoteParameterDC[ParameterEnum = ''DestinationAccount'']/Value/text())[1]', 'varchar(max)') =
columnXML.value('(/ArrayOfNoteParameterDC/NoteParameterDC[ParameterEnum = ''SourceAccount'']/Value/text())[1]', 'varchar(max)')

XML to SQL table (epcis:EPCISDocument)

Could you help me here, please? I need to do a query with an XML file, however, it has something different because it has the epcis: at the beginning of the XML document.
So, if I try to do the query with epcis: and dts: the result is:
Msg 2229, Level 16, State 1, Line 38.
XQuery [nodes()]: The name "epcis" does not denote a namespace.
And if I try to do the query without epcis: and dts:, the result is in blank.
BEGIN
DECLARE #archivo XML
SET #archivo = (
'<?xml version="1.0" encoding="utf-8"?>
<epcis:EPCISDocument xmlns:dts="urn:dts:extension:xsd" schemaVersion="1.2" creationDate="2021-06-30T07:29:32.6511940Z"
xmlns:epcis="urn:epcglobal:epcis:xsd:1">
<EPCISBody>
<EventList>
<ObjectEvent>
<eventTime>2021-06-30T07:29:32</eventTime>
<eventTimeZoneOffset>+02:00</eventTimeZoneOffset>
<action>OBSERVE</action>
<bizStep>code</bizStep>
<dts:epcItemList>
<item>
<epc>123456789</epc>
<code>123456789zz</code>
</item>
<item>
<epc>9687654321</epc>
<code>9687654321zz</code>
</item>
<item>
<epc>147258369</epc>
<code>147258369zz</code>
</item>
</dts:epcItemList>
</ObjectEvent>
</EventList>
</EPCISBody>
</epcis:EPCISDocument>'
)
SELECT
patch.r.value('(epc)[1]', 'varchar(100)') as [epc],
patch.r.value('(code)[1]', 'varchar(100)') as [code]
FROM
#archivo.nodes('EPCISDocument/EPCISBody/EventList/ObjectEvent/epcItemList/item') AS patch(r)
END
GO
I need to export the information as a SQL table per item, like this:
| epc | code |
| -------- | ------------ |
| 123456789 | 123456789zz |
| 9687654321 | 9687654321zz |
enter image description here
enter image description here
Thank you so much
Juan
You need to declare your namespaces. The namespace aliases present in the XML are not relevant, you can use any alias you like (that's the bit after AS in the declaration).
Also, text() is more performant when used in .value
WITH XMLNAMESPACES (
'urn:epcglobal:epcis:xsd:1' AS epcis,
'urn:dts:extension:xsd' AS dts
)
SELECT
patch.r.value('(epc/text())[1]', 'varchar(100)') as [epc],
patch.r.value('(code/text())[1]', 'varchar(100)') as [code]
FROM #archivo.nodes('epcis:EPCISDocument/EPCISBody/EventList/ObjectEvent/dts:epcItemList/item') as patch(r);
SQL Fiddle

How to select values between a XML tag in SQL Query

I have a table with CLOB column storing a XML. The structure of XML is unreadable. I want to get values between few tags like <DOMAINID>; sample is shown below.
XML:
<ID>
<DOMAIN>IND<DOMAIN>
<DOMAINID>112AC<DOMAINID>
<ID>
<GROUP>
<GP>ASIA<GP>
<RSN>GOOD<RSN>
<GROUP>
I am using this:
SELECT REGEXP_REPLACE(COL,'^.*<DOMAINID>(.*)</DOMAINID>.*$','\1',1,0,'mn') col1 FROM tab;
Expected result:
112AC
Actual XML:
<?xml version="1.0" encoding="US-ASCII"?>
<GML:GMMessage
xmlns:GML="GML"
xmlns:GMLType="GML.Type"
xsi:schemaLocation="GML ../schema/gml..xsd" SchemaVersion="9.8">
<BusinessHdr>
<busHdr:BusObjectType>ABC</busHdr:BusObjectType>
<busHdr:BusObjectOwner>HDHDH</busHdr:BusObjectOwner>
<busHdr:BusObjectId>DJHDAHDAJHDA</busHdr:BusObjectId>
<busHdr:BusObjectVersion>1</busHdr:BusObjectVersion>
</BusinessHdr>
<Transaction>
<GenericEvent>NEW</GenericEvent>
<Group>
<GroupId>3424234</GroupId>
<Reason>MANUAL</Reason>
</Group>
< xsi:type="mm:MMIam">
<Id>
<Domain>ssdsgdsg</Domain>
<DomainId>123456ACC</DomainId>
<Version>1</Version>
</Id>
<Date>2021-02-01</Date>
</Transaction>
</GML:GMMessage>
Do not use regular expressions to parse XML; use a proper XML parser.
However, what you have is not properly formed XML as it is missing a root element and you are missing the / in all of the closing tags; so you first need to fix your XML and give it a root element and then you can parse it using an XML parser.
SELECT x.*
FROM table_name t
CROSS APPLY XMLTABLE(
'//root'
PASSING XMLTYPE( '<root>' || t.data || '</root>' )
COLUMNS
domain VARCHAR2(10) PATH './ID/DOMAIN',
domainid VARCHAR2(10) PATH './ID/DOMAINID',
gp VARCHAR2(50) PATH './GROUP/GP',
rsn VARCHAR2(50) PATH './GROUP/RSN'
) x
Which, for the sample data:
CREATE TABLE table_name ( data ) AS
SELECT '<ID>
<DOMAIN>IND</DOMAIN>
<DOMAINID>112AC</DOMAINID>
</ID>
<GROUP>
<GP>ASIA</GP>
<RSN>GOOD</RSN>
</GROUP>' FROM DUAL
Outputs:
DOMAIN | DOMAINID | GP | RSN
:----- | :------- | :--- | :---
IND | 112AC | ASIA | GOOD
If you just want a single value then you can use XMLQUERY:
SELECT XMLQUERY(
'/root/ID/DOMAINID/text()'
PASSING XMLTYPE( '<root>'||data||'</root>' )
RETURNING CONTENT
) AS domainid
FROM table_name
Which outputs:
| DOMAINID |
| :------- |
| 112AC |
db<>fiddle here
Update
I am going to assume that your XML also defines the xsi and busHdr namespaces (if it doesn't then Oracle will fail to parse the XML as it does not know what those namespaces are); that would give you this sample data:
CREATE TABLE table_name ( data ) AS
SELECT '<?xml version="1.0" encoding="US-ASCII"?>
<GML:GMMessage
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:GML="GML"
xmlns:busHdr="busHdr"
xmlns:GMLType="GML.Type"
xsi:schemaLocation="GML ../schema/gml.xsd busHdr ../schema/bushdr.xsd"
SchemaVersion="9.8">
<BusinessHdr>
<busHdr:BusObjectType>ABC</busHdr:BusObjectType>
<busHdr:BusObjectOwner>HDHDH</busHdr:BusObjectOwner>
<busHdr:BusObjectId>DJHDAHDAJHDA</busHdr:BusObjectId>
<busHdr:BusObjectVersion>1</busHdr:BusObjectVersion>
</BusinessHdr>
<Transaction>
<GenericEvent>NEW</GenericEvent>
<Group>
<GroupId>3424234</GroupId>
<Reason>MANUAL</Reason>
</Group>
<Id>
<Domain>ssdsgdsg</Domain>
<DomainId>123456ACC</DomainId>
<Version>1</Version>
</Id>
<Date>2021-02-01</Date>
</Transaction>
</GML:GMMessage>' FROM DUAL
Then, you just need to add the namespace that you are using and update the paths to the new (case-sensitive) locations:
SELECT x.*
FROM table_name t
CROSS APPLY XMLTABLE(
XMLNAMESPACES( 'GML' AS "GML" ),
'//GML:GMMessage/Transaction'
PASSING XMLTYPE( t.data )
COLUMNS
domain VARCHAR2(10) PATH './Id/Domain',
domainid VARCHAR2(10) PATH './Id/DomainId',
version NUMBER(3,0) PATH './Id/Version',
groupid VARCHAR2(50) PATH './Group/GroupId',
reason VARCHAR2(50) PATH './Group/Reason',
dt DATE PATH './Date'
) x
Outputs:
DOMAIN | DOMAINID | VERSION | GROUPID | REASON | DT
:------- | :-------- | ------: | :------ | :----- | :--------
ssdsgdsg | 123456ACC | 1 | 3424234 | MANUAL | 01-FEB-21
db<>fiddle here
Good to see your thinking with your approach...
Would suggest checking out this tool (if you haven't got a similar one) to help you with Regular expressions https://regexr.com/, helps me a lot.
You're SQL looks right (using the "m" and "n" flag for multiline), but not sure if it's your XML was typed in wrong, since you're regex string doesn't work on the XML you pasted, but I did get it work if it's XML.
What is you're current output from your SQL? you might need to use $1 in place of your \1.
I would also suggest
perhaps also escaping your forward slash, as that might be your culprit.
add more specificity to your capture to stop your search from being greedy.
SELECT REGEXP_REPLACE(COL,'^.*<DOMAINID>([0-9A-z]+)<\/DOMAINID>.*$','$1',1,0,'mn') col1 FROM tab;

Extracting multiple values from BLOB as XML

I have an XML like this in a BLOB column:
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>
As you can see the year can be either in the event level or at country level. There can be multiple events and multiple countries per event. This whole XML is stored in a BLOB column in Oracle. I need to extract the value of the year or better check if the year is 2000 and if so return the primary key of the row.
I used EXISTSNODE to check if the year tag is present.
select pk from table where XMLType(blobdata, nls_charset_id('UTF8')).EXISTSNODE('/Document/history/Event[*]/year',
'xmlns="urn:xyzns"') = 1 and EXTRACTVALUE(XMLTYPE(blobdata, nls_charset_id('UTF8')), '/Document/history/Event[*]/year/text()',
'xmlns="urn:xyzns"') = '2000';
However this fails and the extractvalue query returns multiple nodes, so I changed the parameter to '/Document/history/Event[1]/year/text()' to check and it works. However this wouldnt be enough as it only checks the first event tag.
I looked at other questions here and one of the options was to use XMLTABLE since extractvalue is deprecated. I am having trouble understanding the parameters given inside the XMLTABLE. Could someone explain how to use XMLTABLE in this scenario? I should point out that the original datatype is BLOB and not CLOB. Thank you.
Use XMLTABLE to get values for both locations and then use COALESCE to show whichever is not NULL:
SELECT COALESCE( year, country_year ) AS year,
location,
type
FROM table_name t
CROSS APPLY XMLTABLE(
XMLNAMESPACES( DEFAULT 'urn:xyzns' ),
'/document/history/Event'
PASSING XMLTYPE(t.blobdata, nls_charset_id('UTF8'))
COLUMNS
year NUMBER(4,0) PATH './year',
country_year NUMBER(4,0) PATH './Country/year',
location VARCHAR2(200) PATH './Country/Location',
type VARCHAR2(200) PATH './Country/Type'
) x
Which, for the sample data:
CREATE TABLE table_name ( blobdata BLOB );
INSERT INTO table_name
VALUES (
UTL_RAW.CAST_TO_RAW(
'<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>'
)
);
Outputs:
YEAR | LOCATION | TYPE
---: | :------- | :------------
1983 | Lisbon | Political
2016 | USA | Entertainment
db<>fiddle here

How to extract value of all the child nodes of a specific node (provided through input parameter) from XMLType Column in Oracle

One of our requirements is to get the value of all the child nodes of a given specific XML node.
I have got a solution for this using Microsoft SQL Server but I need the same in Oracle. Please see the below query.
Note: incase if there is more than one child node, the result should be the concatenation of all the individual child nodes'value.
select
REPLACE(Properties, 'utf-8', 'utf-16'),
CAST(REPLACE(Col1, 'utf-8', 'utf-16') as XML).value('(//*[local-name() = sql:variable("#var2")])[1]', 'varchar(200)')
from A
Following is a sample data/row from Col1:
<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="xsd:int">0</Value></ConstantInputProperties>
here A is the table and Col1 is one of the columns of table A.
I tried to convert below Solution but it gives me the XML instead of values.
SELECT col1,
EXTRACT(XMLTYPE(col1), '(/*[local-name()="ConstantInputProperties"][1])')
FROM A
Example:
<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="w3.org/2001/XMLSchema" xmlns:xsi="w3.org/2001/XMLSchema-instance"><Value xsi:type="ArrayOfInt"><int>0</int><int>1</int></Value></ConstantInputProperties>
Expected Output 01
<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="ArrayOfBoolean"><boolean>true</boolean><boolean>true</boolean><boolean>true</boolean><boolean>true</boolean><boolean>true</boolean></Value></ConstantInputProperties>
Expected Output truetruetruetruetrue
<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="ArrayOfDecimal"><decimal>1.0000000000</decimal></Value></ConstantInputProperties>
Expected Output 1.0000000000
You could manipulate the XML inside an XPath with FLWOR syntax, but you could also use XMLTable to extract all the values; or rather, two XMLTables, one for a singleton element type and a second optional one for array expansion; get all the values as strings; and aggregate the results together:
select a.id,
listagg(coalesce(x1.value, x2.value), ' ')
within group (order by coalesce(x1.n, x2.n)) as result
from a
cross apply xmltable (
'(/*[local-name()=$var1][1])'
passing xmltype(col1), 'ConstantInputProperties' as "var1"
columns
n for ordinality,
value varchar2(30) path 'Value[#xsi:type="xsd:int"]',
array xmltype path 'Value[fn:starts-with(#xsi:type, "ArrayOf")]'
) x1
outer apply xmltable (
'Value/*'
passing array
columns
n for ordinality,
value varchar2(30) path '.'
) x2
group by a.id;
ID | RESULT
-: | :-----------------------
1 | 0
2 | 0 1
3 | true true true true true
4 | 1.0000000000
db<>fiddle
The n for ordinality just gives a numeric value that lets you keep the original sub-element order when aggregating (so you get 0 1 and not 1 0), If you don't want a spaces added to to the aggregated value then just change the second listagg argument from ' ' to null, though then you can't sell the difference between a singleton 10 and a pair of values with 1 and 0, so that doesn't seem very useful - not that an aggregated value seems that useful anyway really.
You could split into multiple sub-XMLTables, but that's probably not going to gain you anything here; db<>fiddle for info though.
can you suggest how to pass the ConstantInputProperties value as an argument and use it as a variable in function input in this case EXTRACT(XMLTYPE(col1), '(/[local-name()="ConstantInputProperties"]//text())')
The extract() function is deprecated. Use XMLQuery instead; for example:
select xmlquery(
'(/*[local-name()=$var1][1])/Value/text()'
passing xmltype(col1), 'ConstantInputProperties' as "var1"
returning content)
from a
Few examples:
with a as (
select q'[<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="xsd:int">0</Value></ConstantInputProperties>
]' col1 from dual
)
select
x.*
from
a,
xmltable(
'//*[local-name()="ConstantInputProperties"][1]'
passing xmltype(a.col1)
columns
res xmltype path '.'
) x;
--Result:
RES
------------------------------------------------------------------------------
<ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="xsd:int">0</Value></ConstantInputProperties>
with a as (
select q'[<?xml version="1.0" encoding="utf-8"?><ConstantInputProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Value xsi:type="xsd:int">0</Value></ConstantInputProperties>
]' col1 from dual
)
select
x.*
from
a,
xmltable(
'//*[local-name()="ConstantInputProperties"]/*/text()'
passing xmltype(a.col1)
columns
res xmltype path '.'
) x;
--Result:
RES
--------------------------------------------------------------
<Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:int">0</Value>