No results using with xmlnamespace exist - sql

I am new to using WITH XMLNAMESPACES and am having some difficulty producing results using the exist() method. The xml column I am querying is as follows:
<Root>
<ProductDescription ProductID="1" ProductName="Road Bike">
<Features>
<Warranty>1 year parts and labor</Warranty>
<Maintenance>3 year parts and labor extended maintenance is
available
</Maintenance>
</Features>
</ProductDescription>
</Root>
I am attempting to return an xml namespace in the result set, working from a modified example from Microsoft. However when I execute the following query I get 0 results. Removing the alias from the WHERE clause however does produce results.
WITH XMLNAMESPACES ('uri' as pd)
SELECT
Trans_ID,
Bike_Sales.query
(
'<ProductDescription Product_ID = "{ sql:column("Trans_ID")}"/>'
) AS Result
FROM XML_EXAMPLE
WHERE
Bike_Sales.exist
(
'/pd:Root/ProductDescription[(pd:Features)]'
) = 1
So basically I just want to return rows where the Features element is present. Any suggestions?
Thanks.

Related

SQL query to get most recent date from XML document

I am doing a SQL query against a column with an XML document located.
The XML document looks like the following.
<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>
As you can see the dates are out of order.
I am looking for a SQL query that will get the most recent date from the XML document (in this case: 2016-12-01).
One way is to read all data and find the maximum externally (external ORDER BY with TOP 1, like in Prdp's answer, or MAX(), eventually with GROUP BY).
Another way is a FLWOR-XQuery:
DECLARE #xml XML=
'<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>';
SELECT #xml.value('max(for $d in /root/date return xs:date($d))','date')
This means:
Take each value in /root/date, return it as date and find the highest!
Both approaches will need to read the whole list, but it should be a bit faster only to look for the maximum value, rather than return a full list and do some external sorting, picking again...
Try this
DECLARE #xml XML
SET #xml = '<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>'
SELECT Top 1 x.col.value('.', 'date') AS dates
FROM #xml.nodes('/root/date') x(col)
ORDER BY dates DESC

How to use XML AUTO with SQL to create a structure with 2 nested tables

thank you for reading.
I'm currently trying to use XML AUTO with 3 tables to produce an xml document with 1 top level and 2 different tables at a sub level.
A simpler example with 2 tables:
SELECT
dbo.SalesOrder.SupplementaryReference AS SupplementaryReference,
dbo.SalesOrder.Address AS Address,
dbo.SalesOrder.Address2 AS Address2,
dbo.SalesOrder.CardCode AS CardCode,
dbo.SalesOrder.DocDate AS DocDate,
dbo.SalesOrder.DocDueDate AS DocDueDate,
dbo.SalesOrderLines.ItemCode AS ItemCode,
dbo.SalesOrderLines.Quantity AS Quantity
FROM
dbo.SalesOrderLines,
dbo.SalesOrder
WHERE
dbo.SalesOrderLines.SupplementaryReference = dbo.SalesOrder.SupplementaryReference
FOR XML AUTO, ELEMENTS, ROOT('root')
This produces an output xml document like this:
<root>
<dbo.SalesOrder>
<Address>...</Address>
<Address2>...</Address2>
.....
.....
<dbo.SalesOrderLines>
<ItemCode>...</ItemCode>
<Quantity>...</Quantity>
</dbo.SalesOrderLines>
</dbo.SalesOrder>
</root>
This is what I would expect, however I'm attempting to get an XML document as describe below from 3 tables SalesOrder, SalesOrderLines, and SalesOrderExpenses:
<root>
<dbo.SalesOrder>
<Address>...</Address>
<Address2>...</Address2>
.....
.....
<dbo.SalesOrderLines>
<ItemCode>...</ItemCode>
<Quantity>...</Quantity>
</dbo.SalesOrderLines>
<dbo.SalesOrderExpenses>
<BaseType>...</BaseType>
<ExpnsCode>...</ExpnsCode>
<LineTotal>...</LineTotal>
</dbo.SalesOrderExpenses>
</dbo.SalesOrder>
</root>
I have attempted doing the following query:
SELECT
dbo.SalesOrder.SupplementaryReference AS SupplementaryReference,
dbo.SalesOrder.Address AS Address,
dbo.SalesOrder.Address2 AS Address2,
dbo.SalesOrder.CardCode AS CardCode,
dbo.SalesOrder.DocDate AS DocDate,
dbo.SalesOrder.DocDueDate AS DocDueDate,
dbo.SelesOrderExpenses.BasteType AS BaseType,
dbo.SelesOrderExpenses.ExpnsCode AS ExpnsCode,
dbo.SelesOrderExpenses.LineTotal AS LineTotal,
dbo.SalesOrderLines.ItemCode AS ItemCode,
dbo.SalesOrderLines.Quantity AS Quantity
FROM
dbo.SalesOrderLines,
dbo.SalesOrderExpenses,
dbo.SalesOrder
WHERE
dbo.SalesOrderLines.SupplementaryReference = dbo.SalesOrder.SupplementaryReference
FOR XML AUTO, ELEMENTS, ROOT('root')
however I receive this output which nests the third table inside the second:
<root>
<dbo.SalesOrder>
<Address>...</Address>
<Address2>...</Address2>
.....
.....
<dbo.SalesOrderExpenses>
<BaseType>...</BaseType>
<ExpnsCode>...</ExpnsCode>
<LineTotal>...</LineTotal>
<dbo.SalesOrderLines>
<ItemCode>...</ItemCode>
<Quantity>...</Quantity>
</dbo.SalesOrderLines>
</dbo.SalesOrderExpenses>
</dbo.SalesOrder>
</root>
So any ideas?
Thank you for your time.
Without sample data it's a bit hard to give advice, but you might be looking for something like this:
SELECT so.[Address]
,so.Address2
/*more columns*/
,(
SELECT sol.ItemCode
,sol.Quantity
FROM dbo.SalesOrderLines AS sol
WHERE so.SupplementaryReference =sol.SupplementaryReference
FOR XML PATH('dbo.SalesOrderLines'),TYPE --only one row???
) AS [node()]
,(
SELECT sol.ItemCode
,sol.Quantity
FROM dbo.SalesOrderExpenses AS soe
WHERE so.SupplementaryReference =soe.SupplementaryReference
FOR XML PATH('dbo.SalesOrderLines'),TYPE --only one row???
) AS [node()]
FROM dbo.SalesOrder AS so
Btw: Avoid old fashioned joins where you add table names with comma and set the joining predicate in the WHERE clause. Better use the appropriate JOIN. In this case this an INNER JOIN...

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)

extracting all tags(duplicates also) with specified name from xmltype column in sql

i want to extract a tag from an xml and insert into another table.
this XML is having different name spaces hence i use local-name() to fetch the tag which i want.
but some times there are multiple tags with same name. hence its failing with EXACTFETCH RETURNS MULTIPLE NODES. when multiple tags are existed i want to consider both instead of ignoring the second occurence.
source_table(id, payload):
id : 10
payload :
<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>
i want to extract stauscode and message and insert into another table
destination_table(id,name,statuscode,message)
output
10,john,1,one
10,john,2,two
below is the query i used
select id,
extract(payload, '//*[local-name()="name"]'),
extract(payload, '//*[local-name()="statuscode"]'),
extract(payload, '//*[local-name()="statusmessage"]')
from source_table;
i can get first occurence or second occurence by specifying [1] or [2] but i need both the stauscodes to be displayed like below
10,john,1,one
10,john,2,two
any help here
Hope this is what you need: Just past this into an empty query window and execute. Adapt it for your needs:
This solution assumes, that the status codes are sorted (as in your example 1,2,...). If this could occur in random order, just ask again...
Short explanation: The CTE "NameAndCode" brings up the name and the statuscodes. The ROW_NUMBER-function give us the node's index. This index I use to fetch the right message.
One more hint: If you can change the XML's format, it would be better to make the message an attribut of statuscode or to have it as subnode...
DECLARE #xmlColumn XML='<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>';
WITH NameAndCode AS
(
SELECT #xmlColumn.value('(/root/name)[1]','varchar(max)') AS Name
,x.y.value('.','int') AS Statuscode
,x.y.query('..') XMLNode
,ROW_NUMBER() OVER(ORDER BY x.y.value('.','int')) AS StatusIndex
FROM #xmlColumn.nodes('/root/status/statuscode') AS x(y)
)
SELECT *
,XMLNode.value('(/status[1]/statusmessage[sql:column("StatusIndex")])[1]','varchar(max)')
FROM NameAndCode

Extracting XML data in SQL - too many cross apply statements

I have an xml document containing details from a Statement:
<Statement>
<Id />
<Invoices>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
</Invoices>
</Statement>
This works fine for the Statement specific details:
SET #statementId = #xml.value('(Id)[1]', 'UNIQUEIDENTIFIER');
but it requires a singleton, and only returns the first value. I need ALL of the values for the invoices, not just the first so a singleton won't work.
I am able to get the information out using cross apply statements like this:
SELECT
#statementId AS STATEMENT_ID
Id.value('.', 'uniqueidentifier') AS INVOICE_ID
Date.value('.', 'smalldatetime') AS INVOICE_DATE
Due.value('.', 'decimal') AS INVOICE_AMOUNT_DUE
FROM #xml.nodes('Statement') A(S)
cross apply S.nodes('Invoices/Invoice') B(InvoiceD)
cross apply InvoiceD.nodes('Id') C(Id)
cross apply InvoiceD.nodes('Date') D(Date)
cross apply InvoiceD.nodes('AmountDue') E(Due)
This returns an Id, date, and amount from each Invoice in the Statement - perfect.
My problem comes when I try to extract all of the invoice details. I currently have seven cross apply statements and I got the following message:
"The query processor ran out of internal resources and could not
produce a query plan. This is a rare event and only expected for
extremely complex queries or queries that reference a very large
number of tables or partitions. Please simplify the query. If you
believe you have received this message in error, contact Customer
Support Services for more information."
What I want to do is have one cross apply for the Invoice and narrow down the exact field in the select statement, but unless I use '.' I must make the statement return a singleton and I don't get all of the data that I need.
I have done some research about specifying a namespace within the select statement, but all of the examples set the namespace to be an http address instead of a node in an xml document and I haven't gotten anything to return yet using this approach.
The result I'm looking for is something like this, but with more Invoice Details:
STATEMENT_ID INVOICE_ID INVOICE_DATE INVOICE_AMOUNT_DUE ...
Statement-1-Id Invoice-1-Id Invoice-1-Date Invoice-1-AmountDue ...
Statement-1-Id Invoice-2-Id Invoice-2-Date Invoice-2-AmountDue ...
Statement-1-Id Invoice-3-Id Invoice-3-Date Invoice-3-AmountDue ...
Where should I go from here?
EDIT: I removed some unnecessary information. Getting all of the invoice-specific details is my goal here.
select #XML.value('(Statement/Id/text())[1]', 'uniqueidentifier') as StatementId,
T.N.value('(Id/text())[1]', 'uniqueidentifier') as InvoiceId,
T.N.value('(Date/text())[1]', 'smalldatetime') as InvoiceDate,
T.N.value('(AmountDue/text())[1]', 'decimal') as AmountDue
from #XML.nodes('/Statement/Invoices/Invoice') as T(N)
.nodes will shred your XML to rows so that each row T.N is pointing to an Invoice node of its own. On that node there is only a single Id node so fetching the value specifying a singleton Id[1] works.
You can use Id[1] or (Id/text())[1] but the latter will give you a more efficient execution plan.