Navigate multiple nodes Xquery xml SQL Server 2019 - sql

Hi i have the current XML:
<?xml version="1.0" encoding="utf-8"?>
<tcpos-export>
<transactions>
<transaction>
<code>1</code>
<description>MAXI MERCADO SELECTO</description>
</shop>
<code>2</code>
<description>MAÑANITA PANADERIA</description>
</till>
<cashier>
<code>2004</code>
<description>NELSON PANADERIA</description>
<first-name>NELSON PANADERIA</first-name>
</cashier>
<trans-item type="article">
<hash-code>832129</hash-code>
<code>30237</code>
<quantity>1</quantity>
<unit-price>0.50</unit-price>
<total-price>0.50</total-price>
</trans-item>
<payment type="cash">
<code>01</code>
<description>Efectivo</description>
<description-translations />
<amount>0.50</amount>
<prepayment>false</prepayment>
<signature-required>false</signature-required>
<payment-timestamp>18.08.2021 06:51:27</payment-timestamp>
<cash-change>4.50</cash-change>
<cash-given>5.00</cash-given>
</payment>
with this xquery sql 2019:
--procesar articulos del dia de venta
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(till/code/text())[1]','varchar(100)') caja,
trans.value('(cashier/code/text())[1]','varchar(100)') cajero,
trans.value('(beginning-timestamp/text())[1]','varchar(100)') fecha,
trans.value('(trans-num/text())[1]','varchar(100)') transaccion,
item.value('(code/text())[1]','varchar(100)') itemcode,
item.value('(description/text())[1]','varchar(100)') description,
item.value('(hash-code/text())[1]','int') hashcode
FROM [dbo].[XmlImport] xi
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x1(trans)
CROSS APPLY x1.trans.nodes('trans-item[
hash-code/text() and
not( unit-price[contains(text()[1], "-")] ) and
not( taxable-amount[contains(text()[1], "-")] ) and
not( delete-operator-id/text() )
]') x2(item)
CROSS APPLY (VALUES (
item.value('(quantity/text())[1]','numeric(10,3)'),
item.value('(weight/text())[1]','numeric(10,3)'),
item.value('(vat-code/text())[1]','varchar(100)')
) ) v(quantity, weight, vatcode)
ORDER BY hashcode;
im already getting those columns correctly from transactions node example cashier, till. In adition im getting info from node. But, im confused in how i can get info from the next node
<payment type="cash">
<code>01</code>
<description>Efectivo</description>
<description-translations />
i would like to get code and description
i tried the following:
adding this as additional cross apply
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x3(payments)
CROSS APPLY x3.payments.nodes('payment[
code/text()
]') x4(payment)
and in select at the end:
payment.value('(code/text())[1]','varchar(100)')
but when i run the query it stays running and running, and got freezed. I dont know what else i can do.
Please give me some tips.
thank you

You have two options:
You can just select the values directly in your select
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(payment/amount/text())[1]','numeric(18,2)') caja,
-- etc....
FROM [dbo].[XmlImport] xi
-- etc....
Alternatively, you can do another CROSS APPLY nodes. This is what you were trying to do, but it should be x1.trans.nodes('payment') because you want to break out each Payment node from transaction, not from the root XML.
Note that the VALUES clause I added in answer to your previous question was needed only because we needed to refer multiple times to the same node.
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(till/code/text())[1]','varchar(100)') caja,
trans.value('(cashier/code/text())[1]','varchar(100)') cajero,
trans.value('(beginning-timestamp/text())[1]','varchar(100)') fecha,
trans.value('(trans-num/text())[1]','varchar(100)') transaccion,
item.value('(code/text())[1]','varchar(100)') itemcode,
item.value('(description/text())[1]','varchar(100)') description,
item.value('(hash-code/text())[1]','int') hashcode
payment.value('(code/text())[1]','int') paymentCode
FROM [dbo].[XmlImport] xi
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x1(trans)
CROSS APPLY x1.trans.nodes('trans-item[
hash-code/text() and
not( unit-price[contains(text()[1], "-")] ) and
not( taxable-amount[contains(text()[1], "-")] ) and
not( delete-operator-id/text() )
]') x2(item)
CROSS APPLY x1.trans.nodes('payment') x3(payment)
ORDER BY hashcode;
Note that if there might be no payment node then you need OUTER APPLY not CROSS APPLY

Related

SQL Server XML - embedding a dataset within an existing node

There are numerous examples of inserting record sets into XML, but they tend to be very basic and do not cover my specific requirement. In the following instance, I want to extract some data to XML and within that, I need a collection of nodes to represent a variable number of records. I know it can be done, as I have an example, but I do not understand how it works. I do understand my code to return the dataset might make no sense, but it is only meant to represent an example of what I am try to achieve.
SELECT 'Node' AS [A/B],
(
SELECT x.Item
FROM (
SELECT 'Line1' AS [Item]
FROM (SELECT 1 AS x) x
UNION
SELECT 'Line2' AS [Item]
FROM (SELECT 1 AS x) x
) x
FOR XML PATH(''), ROOT('Lines'), TYPE
)
FROM (SELECT 1 AS x) x
FOR XML PATH('Demo')
This gives me the following:
<Demo>
<A>
<B>Node</B>
</A>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
</Lines>
</Demo>
What I want is the following:
<Demo>
<A>
<B>Node</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
</Lines>
</A>
</Demo>
Can anyone help or point me to the correct answer please?
Making some assumptions on the structure of your source data that you should be able to easily adjust around, the following script gives you the output you are after, even across multiple Node groups:
Query
declare #t table([Node] varchar(10),Line varchar(10));
insert into #t values
('Node1','Line1')
,('Node1','Line2')
,('Node1','Line3')
,('Node1','Line4')
,('Node2','Line1')
,('Node2','Line2')
,('Node2','Line3')
,('Node2','Line4')
;
with n as
(
select distinct [Node]
from #t
)
select n.[Node] as [B]
,(select t.Line as Item
from #t as t
where t.[Node] = n.[Node]
for xml path(''), root('Lines'), type
)
from n
for xml path('A'), root('Demo')
;
Output
<Demo>
<A>
<B>Node1</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
<Item>Line3</Item>
<Item>Line4</Item>
</Lines>
</A>
<A>
<B>Node2</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
<Item>Line3</Item>
<Item>Line4</Item>
</Lines>
</A>
</Demo>
You need to place A as the node name of the outer query, and Demo as its root.
SELECT 'Node' AS [B],
(
SELECT x.Item
FROM (
SELECT 'Line1' AS [Item]
UNION
SELECT 'Line2' AS [Item]
) x
FOR XML PATH(''), ROOT('Lines'), TYPE
)
FOR XML PATH('A'), ROOT('Demo'), TYPE
SQL Fiddle

How do I get a value from XML column in SQL?

So, I have a table with a large chunk of data stored in XML.
The partial XML schema (down to where I need) looks like this:
<DecisionData>
<Customer>
<SalesAttemptNumber />
<SubLenderID>IN101_CNAC</SubLenderID>
<DecisionType>Decision</DecisionType>
<DealerID />
<CustomerNumber>468195994772076</CustomerNumber>
<CustomerId />
<ApplicationType>Personal</ApplicationType>
<ApplicationDate>9/16/2008 11:32:07 AM</ApplicationDate>
<Applicants>
<Applicant PersonType="Applicant">
<CustNum />
<CustomerSSN>999999999</CustomerSSN>
<CustLastName>BRAND</CustLastName>
<CustFirstName>ELIZABETH</CustFirstName>
<CustMiddleName />
<NumberOfDependants>0</NumberOfDependants>
<MaritalStatus>Single</MaritalStatus>
<DateOfBirth>1/1/1911</DateOfBirth>
<MilitaryRank />
<CurrentAddress>
<ZipCode>46617</ZipCode>
Unfortunately, I am unfamiliar with pulling from XML, and my google-fu has failed me.
select TransformedXML.value('(/DecisionData/Customer/Applicants/Applicant PersonType="Applicant"/CurrentAddress/ZipCode/node())[1]','nvarchar(max)') as zip
from XmlDecisionInputText as t
I believe my problem lies with the portion that goes Applicant PersonType="Applicant", but am unsure how to deal with it.
Thanks for any help.
The xpath in its simplest form would be:
TransformedXML.value('(//ZipCode)[1]', 'nvarchar(100)') AS zip
This will find the first ZipCode node anywhere inside your document. If there are multiple, just be specific (as much as you want but not any more):
TransformedXML.value('(/DecisionData/Customer/Applicants/Applicant[#PersonType="Applicant"]/CurrentAddress/ZipCode)[1]', 'nvarchar(100)') AS zip
DB Fiddle
If there are MULTIPLE applicants, you can use a CROSS APPLY
Example
Select A.ID
,B.*
From XmlDecisionInputText A
Cross Apply (
Select PersonType = x.v.value('#PersonType','VARCHAR(150)')
,CustLastName = x.v.value('CustLastName[1]','VARCHAR(150)')
,CustFirstName = x.v.value('CustFirstName[1]','VARCHAR(150)')
,ZipCode = x.v.value('CurrentAddress[1]/ZipCode[1]','VARCHAR(150)')
From XmlDecisionInputText.nodes('DecisionData/Customer/Applicants/*') x(v)
) B

Flattening xml data in sql

I'm trying to flatten XML data in a SQL query but I always seem to get nulls.
I tried the cross/outer apply method described here.
The column with XML data is called Data.
I'm guessing that the xml data with these links need to be somehow also added?
Could you please help to get a proper SQL query?
Query I tried:
SELECT
v.name
,pref.value('(LocalId/text())[1]', 'nvarchar(10)') as localid
FROM [database].[requests] v
outer apply v.Data.nodes('/DataForm') x(pref)
GO
example of xml data in that column:
<Dataform xmlns="http://somelongasslink.org/hasalsosomestuffhere" xmlns:i="http://somexlmschemalink/">
<DeleteDate xmlns="http://somelongasslink.org/hasalsosomestuffhere" i:nil="true" />
<LocalId xmlns="http://somelongasslink.org/hasalsosomestuffhere">5325325</LocalId>
...
You can use this code to get the result you're looking for:
;WITH XMLNAMESPACES(DEFAULT 'http://somelongasslink.org/hasalsosomestuffhere')
SELECT
rq.Name,
LocalID = TC.value('(LocalId)[1]', 'nvarchar(10)')
FROM
[database].[requests] rq
CROSS APPLY
rq.Data.nodes('/Dataform') AS TX(TC)
GO
There were two problems with your code:
you're not respecting / including the XML namespace that's defined on the XML document
<Dataform xmlns="http://somelongasslink.org/hasalsosomestuffhere"
*******************************************************
you didn't pay attention to the case-sensitivity of XML in your call to .nodes() - you need to use .nodes('/Dataform') (not /DataForm - the F is not capitalized in your XML)

Parsing Dynamic XML to SQL Server tables with Parent and child relation

I have a XML in Source Table. I need to parse this XML to 3 different tables which has Parent Child relationship. I can do this in C# but currently for this i need to implement it at SQL server side.
The sample xml looks like:
<ROWSET>
<ROW>
<HEADER_ID>5001507</HEADER_ID>
<ORDER_NUMBER>42678548</ORDER_NUMBER>
<CUST_PO_NUMBER>LSWQWE1</CUST_PO_NUMBER>
<CUSTOMER_NUMBER>38087</CUSTOMER_NUMBER>
<CUSTOMER_NAME>UNIVERSE SELLER</CUSTOMER_NAME>
<LINE>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532839</LINE_ID>
<LINE_NUMBER>1</LINE_NUMBER>
<ITEM_NUMBER>STAGEPAS 600I-CA</ITEM_NUMBER>
<ORDER_QUANTITY>5</ORDER_QUANTITY>
</LINE_ROW>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532901</LINE_ID>
<LINE_NUMBER>3</LINE_NUMBER>
<ITEM_NUMBER>CD-C600 RK</ITEM_NUMBER>
<ORDER_QUANTITY>6</ORDER_QUANTITY>
</LINE_ROW>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532902</LINE_ID>
<LINE_NUMBER>4</LINE_NUMBER>
<ITEM_NUMBER>CD-S300 RK</ITEM_NUMBER>
<ORDER_QUANTITY>8</ORDER_QUANTITY>
</LINE_ROW>
</LINE>
<PRCADJ>
<PRCADJ_ROW>
<PRICE_ADJUSTMENT_ID>43095064</PRICE_ADJUSTMENT_ID>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532839</LINE_ID>
<ADJUSTED_AMOUNT>-126</ADJUSTED_AMOUNT>
</PRCADJ_ROW>
<PRCADJ_ROW>
<PRICE_ADJUSTMENT_ID>43095068</PRICE_ADJUSTMENT_ID>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532840</LINE_ID>
<ADJUSTED_AMOUNT>-96.6</ADJUSTED_AMOUNT>
</PRCADJ_ROW>
</PRCADJ>
</ROW>
</ROWSET>
The issue is the Parent can have multiple child and each child can multiple sub child. How can i write query to transfer this into Sql Server 2005
You need to use three CROSS APPLY operators to break up the "list of XML elements" into separate pseudo tables of XML rows, so you can access their properties - something like this:
SELECT
HeaderID = XCRow.value('(HEADER_ID)[1]', 'int'),
OrderNumber = XCRow.value('(ORDER_NUMBER)[1]', 'int'),
LineHeaderID = XCLine.value('(HEADER_ID)[1]', 'int'),
LineID = XCLine.value('(LINE_ID)[1]', 'int'),
LineNumber = XCLine.value('(LINE_NUMBER)[1]', 'int'),
PriceAdjustmentID = XCPrc.value('(PRICE_ADJUSTMENT_ID)[1]', 'int'),
AdjustedAmount = XCPrc.value('(ADJUSTED_AMOUNT)[1]', 'decimal(20,4)')
FROM
dbo.YourTableNameHere
CROSS APPLY
Data.nodes('/ROWSET/ROW') AS XTRow(XCRow)
CROSS APPLY
XCRow.nodes('LINE/LINE_ROW') AS XTLine(XCLine)
CROSS APPLY
XCRow.nodes('PRCADJ/PRCADJ_ROW') AS XTPrc(XCPrc)
With this, the first CROSS APPLY will handle all the elements that are found directly under <ROWSET> / <ROW> (the header information), the second one will enumerate all instances of <LINE> / <LINE_ROW> below that header element, and the third CROSS APPLY handles the <PRCADJ> / <PRCADJ_ROW> elements, also below the header.
You might need to tweak the outputs a bit - and I only picked two or three of the possible values - extend and adapt to your own needs! But this should show you the basic mechanism - the .nodes() method returns a "pseudo table" of XML fragments, one for each match of the XPath expression you define.
you can do some thing like this. using cross apply you will get node elements and then extract the value using value clause. you need to specify the column type i.e int or varchar etc.
The result can then be inserted using insert into select query.
insert into Table1 values ( header_id, order_number, cust_po_number)
select R.value('(HEADER_ID)[1]', 'int') As header_id,
R.value('(ORDER_NUMBER)[1]', 'int') as order_number,
R.value('(CUST_PO_NUMBER)[1]', 'varchar(256)') as cust_po_number
from table
cross apply XMLdata.nodes('/ROWSET/ROW') AS P(R)
insert into Table2 values ( header_id, line_id, line_number)
select R.value('(HEADER_ID)[1]', 'int') As header_id,
R.value('(LINE_ID)[1]', 'int') as line_id,
R.value('(LINE_NUMBER)[1]', 'int') as line_number
from table
cross apply XMLdata.nodes('/ROWSET/ROW/LINE/LINE_ROW') AS P(R)

SQL query to upload XML file into a single SQL table

I have a sample xml file that I need to upload into a SQL table.
Below the xml file is the SQL query to select the XML values to insert into the table
Problem: I am unable to obtain the <Actual>,<ExpectedValue> values as they occur more than once
Would appreciate if someone could kindly help correct/direct me to get the right values
Thanks.
<?xml version="1.0" encoding="utf-8" ?>
<Results>
<General>
<Version>1.01</Version>
<InputFilename>TestFile.xml</InputFilename>
<Filename>xyz.XML</Filename>
</General>
<Tests>
<Test Name="Test 1">
<Number>0</Number>
<MinimumSampleCount>1</MinimumSampleCount>
<ExpectedValues>
<ExpectedValue>8</ExpectedValue>
<ExpectedValue>5</ExpectedValue>
</ExpectedValues>
<Actual>
<seq>1</seq>
<ValueFound>8</ValueFound>
<seqTestResult>Passed</seqTestResult>
</Actual>
<Actual>
<seq>3</seq>
<ValueFound>8</ValueFound>
<seqTestResult>Passed</seqTestResult>
</Actual>
<Result>Last sequence matches test cases.</Result>
<TestResult>Passed</TestResult>
</Test>
<Test Name="Test 3">
<Number>25</Number>
<ExpectedValues><ExpectedValue>3.50</ExpectedValue>
<ExpectedValue>3.56</ExpectedValue>
<ExpectedValue>3.60</ExpectedValue>
</ExpectedValues>
<Result>not applicable</Result>
<TestResult>Skipped</TestResult>
</Test>
<OverallTestResult>Passed</OverallTestResult>
</Tests>
</Results>
SQL Code:
INSERT INTO dbo.Results_XML ([Version],[InputFilename],[Filename],
[OverallTestResult],Name,Number,MinimumSampleCount,ActualNumberOfSamples,ExpectedValue,
ActualSeq, ActualValueFound,ActualseqTestResult,CompareValue,Result,TestResult
SELECT [Version] = x.data.value('Version[1]','numeric(3,2)'),
[InputFilename] = x.data.value('InputFilename[1]','varchar(80)'),
[Filename] = x.data.value('Filename[1]','varchar(80)'),
[OverallTestResult]=T2.N.value('OverallTestResult[1]','varchar(15)'),
[Test Name]= y.data.value('#Name','varchar(255)')
,[Number]= y.data.value('Number[1]','int'),
[MinimumSampleCount]=y.data.value('Number[1]','int'),
[ActualNumberOfSamples]=y.data.value('ActualNumberOfSamples[1]','int')
,[ExpectedValue]=z.data.value('ExpectedValue[1]','varchar(255)')
,[ActualSeq]=v.data.value('Seq[1]','int'),
,ActualValueFound=v.data.value('ValueFound[1]','int'),
,ActualseqTestResult=v.data.value('seqTestResult[1]','varchar(255)'),
,[CompareValue]=y.data.value('CompareValue[1]','bigint')
,[Result]=y.data.value('Result[1]','varchar(40)'),
[TestResult]=y.data.value('TestResult[1]','varchar(10)')
FROM CD t
CROSS APPLY t.XMLData.nodes('/Results/General') x(data)
cross apply t.XMLData.nodes('/Results/Tests') as T2(N)
CROSS APPLY t.XMLData.nodes('/Results/Tests/Test') as y(data)
CROSS APPLY t.XMLData.nodes('Test/ExpectedValues/Expected') z(data)
CROSS APPLY t.XMLData.nodes('Test/ExpectedValues/Actual/') v(data)
I'm assuming (based on your previous question) that this is for SQL Server - correct?
(you should always explicitly and clearly specify what database system you're using - get used to it!)
You need to use something like this:
get a XML node for the <Results>/<General> node to get some basic data
then get a list of all <Test> nodes - get some data from there
based on each <Test> node - get a list of it's <ExpectedValues>/<ExpectedValue> nodes as well as the <Actual> nodes inside the <Test> and pick out the data that you need
In my experience, it's a good idea to use descriptive names - use GeneralData, ExpectedData etc. instead of just x, y and z - that makes it a lot easier to understand what you're dealing with!
Here's the T-SQL / XQuery statement that I used:
SELECT
-- General values
[Version] = GeneralData.value('Version[1]','numeric(3,2)'),
[InputFilename] = GeneralData.value('InputFilename[1]','varchar(80)'),
[Filename] = GeneralData.value('Filename[1]','varchar(80)'),
-- Test-specific values
[Test Name]= TestData.value('#Name','varchar(255)'),
[Test Number]= TestData.value('Number[1]','int'),
-- Test-specific expected values
[ExpectedValue] = ExpectedData.value('.', 'varchar(255)'),
-- Test-specific actual values
[ActualSeq] = ActualData.value('(seq)[1]', 'int'),
ActualValueFound = ActualData.value('(ValueFound)[1]', 'int'),
ActualseqTestResult = ActualData.value('(seqTestResult)[1]', 'varchar(255)')
FROM
CD t
CROSS APPLY
-- fetch the <Results> / <General> node
XMLdata.nodes('/Results/General') AS XGD(GeneralData)
CROSS APPLY
-- fetch a list of <Results> / <Tests> / <Test> nodes
XMLDATA.nodes('/Results/Tests/Test') AS XTD(TestData)
CROSS APPLY
-- for each <Test> node - fetch its contained <ExpectedValues>/<ExpectedValue> nodes
TestData.nodes('ExpectedValues/ExpectedValue') AS XED(ExpectedData)
CROSS APPLY
-- for each <Test> node - fetch its contained <Actual> nodes
TestData.nodes('Actual') AS XAD(ActualData)