Getting nth Element from last in a xml in Sql Server - sql

Please consider this XML:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>
I want to write a function that gets a number, and then I get nth Person element, and Name. For example if 0 pass to my function I return Aba, if 1 pass to my function I return Jigha.

This should work. Set the value of the #index variable as the number of the record to find, relative to the end of the list:
declare #index int = 1
declare #xml xml = '<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>'
select t2.person.value('(Name/text())[1]','varchar(50)')
from #xml.nodes('Employees/Person[position()=(last()-sql:variable("#index"))]') as t2(person)

Related

PLSQL - REGEXP_SUBSTR Remove XML Tags

I currently have a payload that was generated by oracle xml gateway that I need to pull some exact information out. The payload information is store within a db table meaning that I am attempting to us regexp_substr to accomplish this task.
This is the tag that is in the middle of the XML document
<IDCODE>S2200</IDCODE>
"<?xml version="1.0" encoding="UTF-8" standalone='no'?>
<!DOCTYPE PROCESS_INVOICE_002 SYSTEM "asfasdf.dtd">
<!-- Oracle eXtensible Markup Language Gateway Server -->
<PROCESS_INVOICE_002>
<CNTROLAREA>
<BSR>
<VERB value="PROCESS"/>
<NOUN value="INVOICE"/>
<REVISION value="002"/>
</BSR>
<SENDER>
<LOGICALID/>
<COMPONENT/>
<TASK/>
<REFERENCEID/>
<CONFIRMATION/>
<LANGUAGE/>
<CODEPAGE/>
<AUTHID/>
</SENDER>
<DATETIME qualifier="CREATION">
<YEAR/>
<MONTH/>
<DAY/>
<HOUR/>
<MINUTE/>
<SECOND/>
<SUBSECOND/>
<TIMEZONE/>
</DATETIME>
</CNTROLAREA>
<DATAAREA>
<PROCESS_INVOICE>
<INVHEADER>
<AMOUNT qualifier="DOCUMENT" type="T" index="1">
<VALUE>78538</VALUE>
<NUMOFDEC>8</NUMOFDEC>
<SIGN>+</SIGN>
<CURRENCY>USD</CURRENCY>
<DRCR>D</DRCR>
</AMOUNT>
<DATETIME qualifier="DOCUMENT" index="1">
<YEAR>2020</YEAR>
<MONTH>11</MONTH>
<DAY>28</DAY>
<HOUR>00</HOUR>
<MINUTE>00</MINUTE>
<SECOND>00</SECOND>
<SUBSECOND>0000</SUBSECOND>
<TIMEZONE>+0000</TIMEZONE>
</DATETIME>
<DOCUMENTID>81989184</DOCUMENTID>
<DESCRIPTN/>
<DOCTYPE>INV</DOCTYPE>
<PAYMETHOD/>
<REASONCODE/>
<USERAREA>
<NOTEREFCODE/>
<NOTESREF/>
<VENDNUMQUAL>IA</VENDNUMQUAL>
<VENDNUM>98181</VENDNUM>
<DEPTNUMQUAL>DP</DEPTNUMQUAL>
<DEPTNUM>85</DEPTNUM>
<ORDNUMQUAL/>
<ORDNUM>0</ORDNUM>
<CUSTCODEQUAL/>
<CUSTCODE/>
<NETDAYS/>
<DATETIMEQUAL/>
<FOBCODE/>
<UOM/>
<TOTALQUANTITY/>
</USERAREA>
<PARTNER>
<NAME index="1">COMPANY NAME</NAME>
<ONETIME/>
<PARTNRID/>
<PARTNRTYPE>Supplier</PARTNRTYPE>
<SYNCIND/>
<ACTIVE/>
<CURRENCY/>
<DESCRIPTN/>
<DUNSNUMBER/>
<GLENTITYS/>
<PARENTID/>
<PARTNRIDX/>
<PARTNRRATG/>
<PARTNRROLE/>
<PAYMETHOD/>
<TAXEXEMPT/>
<TAXID/>
<TERMID/>
<USERAREA>
<IDQUAL/>
<IDCODE/>
</USERAREA>
<CONTACT>
<NAME index="1">PROFILE</NAME>
<CONTCTTYPE/>
<DESCRIPTN/>
<EMAIL/>
<FAX index="1"/>
<TELEPHONE index="1"/>
<USERAREA/>
</CONTACT>
</PARTNER>
<PARTNER>
<NAME index="1">CUSTOMER NAME</NAME>
<ONETIME/>
<PARTNRID>981698198</PARTNRID>
<PARTNRTYPE>ShipTo</PARTNRTYPE>
<SYNCIND/>
<ACTIVE/>
<CURRENCY/>
<DESCRIPTN/>
<DUNSNUMBER/>
<GLENTITYS/>
<PARENTID/>
<PARTNRIDX/>
<PARTNRRATG/>
<PARTNRROLE/>
<PAYMETHOD/>
<TAXEXEMPT/>
<TAXID/>
<TERMID/>
<USERAREA>
<IDQUAL>ZZ</IDQUAL>
<IDCODE>S2200</IDCODE>
</USERAREA>
<ADDRESS>
<ADDRLINE index="1">123 MAIN STREET</ADDRLINE>
<ADDRTYPE/>
<CITY>HAM CITY</CITY>
<COUNTRY>United States</COUNTRY>
<COUNTY>NEW YORK</COUNTY>
<DESCRIPTN/>
<FAX index="1"/>
<POSTALCODE>18080</POSTALCODE>
<REGION/>
<STATEPROVN>NY</STATEPROVN>
<TAXJRSDCTN/>
<TELEPHONE index="1"/>
<URL/>
<USERAREA/>
</ADDRESS>
REGEX that I am using in the query
TRIM(regexp_substr(ed.payload, '?.+(</IDCODE>)')) Store_NUM,
TRIM(regexp_substr(ed.payload, '(^IDCODE)?.+(</IDCODE>)')) Store_Number
The Outcome that I am receiving from the above SQL regexp_substr. The issue is that I have made it to the correct tab but I can't figure out how to strip the \<IDCODE> and the \</IDCODE> for the output
-Field can have 4 or 5 chars
-letters or numbers
<IDCODE>S2200</IDCODE> Store_NUM
<IDCODE>S2200</IDCODE> Store_Number
I believe you are looking for this if I am understanding you correctly. Return everything in the group between the tags.
SELECT REGEXP_SUBSTR('<IDCODE>S2200</IDCODE>', '<IDCODE>(.*)</IDCODE>', 1, 1, NULL, 1) Store_Number
from dual;
STORE_NUMBER
------------
S2200
1 row selected.

Extracting XML data using SQL

I would like to be able to extract specific data from a XML type using Oracle in my example for the customer named "Arshad Ali"
This is my xml data that was inserted:
<Customers>
<Customer CustomerName="Arshad Ali" CustomerID="C001">
<Orders>
<Order OrderDate="2012-07-04T00:00:00" OrderID="10248">
<OrderDetail Quantity="5" ProductID="10" />
<OrderDetail Quantity="12" ProductID="11" />
<OrderDetail Quantity="10" ProductID="42" />
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
<Customer CustomerName="Paul Henriot" CustomerID="C002">
<Orders>
<Order OrderDate="2011-07-04T00:00:00" OrderID="10245">
<OrderDetail Quantity="12" ProductID="11" />
<OrderDetail Quantity="10" ProductID="42" />
</Order>
</Orders>
<Address> Address line 5, 6, 7</Address>
</Customer>
<Customer CustomerName="Carlos Gonzlez" CustomerID="C003">
<Orders>
<Order OrderDate="2012-08-16T00:00:00" OrderID="10283">
<OrderDetail Quantity="3" ProductID="72" />
</Order>
</Orders>
<Address> Address line 1, 4, 5</Address>
</Customer>
</Customers>
</ROOT>
using get clob I was able to extract all of the customers.
Was wondering if anyone could help me extract data for a specific customer.. tried using the following but was unsuccessful
SELECT extract(OBJECT_VALUE, '/root/Customers') "customer"
FROM mytable2
WHERE existsNode(OBJECT_VALUE, '/customers[CustomerName="Arshad Ali" CustomerID="C001"]')
= 1;
The case and exact names of the XML nodes matter:
SELECT extract(OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]') "customer"
FROM mytable2
WHERE existsnode (OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]') = 1
db<>fiddle
If you only want to search by name then only use that attribute:
SELECT extract(OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"]') "customer"
FROM mytable2
WHERE existsnode (OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"]') = 1
But extract() and existsnode() are deprecated; use xmlquery() and xmlexists() instead:
SELECT xmlquery('/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]'
passing object_value
returning content) "customer"
FROM mytable2
WHERE xmlexists('/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]'
passing object_value)
db<>fiddle

Sql return results as xml

Given this data:
FName Lname ApartmentNumber
-----------------------------------
David Shumer 1
John Deer 1
Mark Ratz 2
Steven Styer 2
I would like to return it as xml, so I did this:
select * from Table1
for xml raw('person'), root('PeopleInApartment'), elements
And I got this result
<PeopleInApartment>
<Person>
<FName>David</FName>
<LName>Shumer</LName>
<ApartmentNumber>1</ApartmentNumber>
</Person>
<Person>
<FName>John</FName>
<LName>Deer</LName>
<ApartmentNumber>1</ApartmentNumber>
</Person>
<Person>
<FName>Mark</FName>
<LName>Ratz</LName>
<ApartmentNumber>2</ApartmentNumber>
</Person>
<Person>
<FName>Steven</FName>
<LName>Styer</LName>
<ApartmentNumber>2</ApartmentNumber>
</Person>
</PeopleInApartment>
Is it possible to group people by apartment so the result would look like this?:
<Apartments>
<PeopleInApartment>
<Person>
<FName>David</FName>
<LName>Shumer</LName>
<ApartmentNumber>1</ApartmentNumber>
</Person>
<Person>
<FName>John</FName>
<LName>Deer</LName>
<ApartmentNumber>1</ApartmentNumber>
</Person>
</PeopleInApartment>
<PeopleInApartment>
<Person>
<FName>Mark</FName>
<LName>Ratz</LName>
<ApartmentNumber>2</ApartmentNumber>
</Person>
<Person>
<FName>Steven</FName>
<LName>Styer</LName>
<ApartmentNumber>2</ApartmentNumber>
</Person>
</PeopleInApartment>
</Apartments>
Sql solution. Note subquery must depend only on outer columns in the GROUP BY, t1.ApartmentNumber specifically.
select t1.ApartmentNumber
, (select t2.FName, t2.Lname
from Table1 t2
where t2.ApartmentNumber = t1.ApartmentNumber
for xml path('Person'), type
) as PeopleInApartment
from Table1 t1
group by ApartmentNumber
for xml path('Apartment'), root('Apartments'), elements
Returns data apartment - wise
<Apartments>
<Apartment>
<ApartmentNumber>1</ApartmentNumber>
<PeopleInApartment>
<Person>
<FName>David </FName>
<Lname>Shumer</Lname>
</Person>
<Person>
<FName>John </FName>
<Lname>Deer </Lname>
</Person>
</PeopleInApartment>
</Apartment>
<Apartment>
<ApartmentNumber>2</ApartmentNumber>
<PeopleInApartment>
<Person>
<FName>Mark </FName>
<Lname>Ratz </Lname>
</Person>
<Person>
<FName>Steven</FName>
<Lname>Styer </Lname>
</Person>
</PeopleInApartment>
</Apartment>
</Apartments>
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication3
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement apartments = new XElement("Apartments");
var groups = doc.Descendants("Person")
.GroupBy(x => (int)x.Element("ApartmentNumber"))
.ToList();
foreach (var group in groups)
{
XElement peopleInApartment = new XElement("PeopleInApartment", group);
apartments.Add(peopleInApartment);
}
}
}
}
Please check Below :
select
(select t2.FName, t2.Lname, t1.ApartmentNumber
from #TEMP t2
where t2.ApartmentNumber = t1.ApartmentNumber
for xml path('Person'), type
) as PeopleInApartment
from #TEMP t1
group by ApartmentNumber
for xml path(''), root ('Apartments')

Find element or attribute value anywhere in XML

I am trying to find the value of an element / attribute regardless of where it exists in the XML.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<cXML payloadID="12345677-12345567" timestamp="2017-07-26T09:11:05">
<Header>
<From>
<Credential domain="1212">
<Identity>01235 </Identity>
<SharedSecret/>
</Credential>
</From>
<To>
<Credential domain="1212">
<Identity>01234</Identity>
</Credential>
</To>
<Sender>
<UserAgent/>
<Credential domain="8989">
<Identity>10678</Identity>
<SharedSecret>Testing123</SharedSecret>
</Credential>
</Sender>
</Header>
<Request deploymentMode="Prod">
<ConfirmationRequest>
<ConfirmationHeader noticeDate="2017-07-26T09:11:05" operation="update" type="detail">
<Total>
<Money>0.00</Money>
</Total>
<Shipping>
<Description>Delivery</Description>
</Shipping>
<Comments>WO# generated</Comments>
</ConfirmationHeader>
<OrderReference orderDate="2017-07-25T15:22:11" orderID="123456780000">
<DocumentReference payloadID="5678-4567"/>
</OrderReference>
<ConfirmationItem quantity="1" lineNumber="1">
<ConfirmationStatus quantity="1" type="detail">
<ItemIn quantity="1">
<ItemID>
<SupplierPartID>R954-89</SupplierPartID>
</ItemID>
<ItemDetail>
<UnitPrice>
<Money currency="USD">0.00</Money>
</UnitPrice>
<Description>Test Descritpion 1</Description>
<UnitOfMeasure>QT</UnitOfMeasure>
</ItemDetail>
</ItemIn>
</ConfirmationStatus>
</ConfirmationItem>
<ConfirmationItem quantity="1" lineNumber="2">
<ConfirmationStatus quantity="1" type="detail">
<ItemIn quantity="1">
<ItemID>
<SupplierPartID>Y954-89</SupplierPartID>
</ItemID>
<ItemDetail>
<UnitPrice>
<Money currency="USD">0.00</Money>
</UnitPrice>
<Description>Test Descritpion 2</Description>
<UnitOfMeasure>QT</UnitOfMeasure>
</ItemDetail>
</ItemIn>
</ConfirmationStatus>
</ConfirmationItem>
</ConfirmationRequest>
</Request>
</cXML>
I want to get the value of the payloadID on the DocumentReference element. This is what I have tried so far:
BEGIN
Declare #Xml xml
Set #Xml = ('..The XML From Above..' as xml)
END
--no value comes back
Select c.value('(/*/DocumentReference/#payloadID)[0]','nvarchar(max)') from #Xml.nodes('//cXML') x(c)
--no value comes back
Select c.value('#payloadID','nvarchar(max)') from #Xml.nodes('/cXML/*/DocumentReference') x(c)
--check if element exists and it does
Select #Xml.exist('//DocumentReference');
I tried this in an xPath editor: //DocumentReference/#payloadID
This does work, but I am not sure what the equivalent syntax is in SQL
Calling .nodes() (like suggested in comment) is an unecessary overhead...
Better try it like this:
SELECT #XML.value('(//DocumentReference/#payloadID)[1]','nvarchar(max)')
And be aware, that XPath starts counting at 1. Your example with [0] cannot work...
--no value comes back
Select c.value('(/*/DocumentReference/#payloadID)[0]','nvarchar(max)') from...

insert an element to nth element in a xml in Sql Server

please consider this XML:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>
I want to write a procedure that get a number and then I insert an element to nth Person element. for example if 1 pass to my procedure I insert an element to first person element.
DECLARE #data XML =
'
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>
'
DECLARE #offset INT = 2
DECLARE #value VARCHAR(100) = 'newvalue'
SET #data.modify('insert <NewAttribute>{sql:variable("#value")}</NewAttribute> as last into (/Employees/Person)[sql:variable("#offset")][1]')
SELECT #data
DECLARE #AttributeValue NVARCHAr(100) = 'TestValue';
DECLARE #NodeNR INT = 3
DECLARE #XML XML = '<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>';
SET #XML.modify('insert attribute Attribute {sql:variable("#AttributeValue")} into (/Employees/Person[position()=sql:variable("#NodeNr")])[1]')
Try this one -
DECLARE #Param INT = 1000
DECLARE #NodeID INT = 2
DECLARE #XML XML = '
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>'
DECLARE #SQL NVARCHAR(MAX) = '
DECLARE #XML XML = #XML_Param
DECLARE #Id INT = #Value_Param
SET #XML.modify(''insert attribute Id {sql:variable("#Id")} into (/Employees/Person)[' + CAST(#NodeID AS VARCHAR) + ']'')
SELECT #XML_Output = #XML'
DECLARE #Definition NVARCHAR(500) =
N'#XML_Param XML, #Value_Param INT, #Node_Param INT, #XML_Output XML OUTPUT'
DECLARE #XMLResult XML
EXEC sys.sp_executesql
#SQL
, #Definition
, #XML_Param = #XML
, #Value_Param = #Param
, #Node_Param = 1
, #XML_Output = #XMLResult OUTPUT
SELECT #XMLResult