SQL Query for Attribute Value(s) - sql

I have searched everywhere and seem to be having trouble for my specific issue. I am trying to parse xml values out of our database. The table is named 'Table.XMLfileData', with a column of XMLData. The current setup of that column is as such:
The setup of the XML itself are all nested in attributes:
I want to be able to pull any piece of data out of each of these XML files. The query that I have found in my research should be something like this:
SELECT r.value('#first_name','varchar(60)')
FROM TableName
CROSS APPLY columnname.nodes('Vehicle_Loan/Applicants/Applicant/first_name') AS
x(r)
However I retrieve a blank or null value every time. I am new to this, what am I doing wrong?

.value(...) requires a single node to work with and XPath is case sensitive.
SELECT r.value('(./#first_name)[1]','varchar(60)')
FROM TableName
CROSS APPLY columnname.nodes('Vehicle_Loan/Applicants/Applicant') AS
x(r)
... working example ...
DECLARE #xml XML = N'
<Vehicle_Loan>
<Applicants>
<Applicant first_name="Matt" />
<Applicant first_name="Jim" />
</Applicants>
</Vehicle_Loan>
';
SELECT r.value('(./#first_name)[1]','varchar(60)') AS [FirstName]
FROM #xml.nodes('Vehicle_Loan/Applicants/Applicant') AS x(r)
... output ...
FirstName
---------------
Matt
Jim

Related

How to read data from multiple XML files in SQL Server?

Background :
I want to obtain data from multiple XML files (stored in database) and fetch them into one result set. The basic working solution, with single XML file looks similar to this one :
DECLARE #xml xml
SET #xml =
(SELECT TOP 1 convert(varchar(max), convert(varbinary(max), [XML_FILE]))
FROM [SOME_TABLE])
SELECT
b.value('(./SomeNode/text())[1]','nvarchar(100)')) as [Some_Text],
b.value('(./SomeOtherNode/#VAL)[1]','int')) as [Some_Val]
FROM #xml.nodes('Example/File') as a(b)
Obviously this won't work with SELECT that returns many rows (many XML files). Sub-optimal solution could be achieved using cursor (iterating over collection -> pushing data into temporary table -> SELECT (*) FROM temporary_table) however, I believe thats not necessary and more straightforward solution can be achieved.
Question :
How to fetch data from multiple XML files, obtained via SELECT query, into a single result-set, without using cursor?
FILE_NAME || Value 1 || Value 2 || ...
----------------------------------------------
XML_FILE_1 || Node1Value || Node2Value || ...
XML_FILE_2 || Node1Value || Node2Value || ...
I've found solution thanks to #Shnugo answer.
If the type of xml-container column is different then XML MS-SQL dedicated one, then double CROSS APPLY should be performed. Example below :
DECLARE #mockup TABLE(ID INT IDENTITY, [XML_DATA] VARBINARY(MAX));
INSERT INTO #mockup VALUES('<Example><File><SomeNode>blah</SomeNode><SomeOtherNode VAL="1"/></File></Example>')
,('<Example><File><SomeNode>blub</SomeNode><SomeOtherNode VAL="2"/></File></Example>')
SELECT
ID,
b.value('(SomeNode/text())[1]','nvarchar(100)') as [Some_Text],
b.value('(SomeOtherNode/#VAL)[1]','int') as [Some_Val]
FROM #mockup
CROSS APPLY (SELECT CAST(convert(varbinary(max), [XML_DATA]) as XML)) as RAW_XML(xml_field)
CROSS APPLY RAW_XML.xml_field.nodes('Example/File') as a(b)
For sure the CURSOR approach is not needed and would be wrong entirely...
The general approach should be something like this:
SELECT
b.value('(./SomeNode/text())[1]','nvarchar(100)') as [Some_Text],
b.value('(./SomeOtherNode/#VAL)[1]','int') as [Some_Val]
FROM [SOME_TABLE]
CROSS APPLY [XML_FILE].nodes('Example/File') as a(b);
But there are questions open:
Speaking about xml files is a bit bewildering... I hope to get this correctly, that all these XMLs are living in a table's column.
If the first is true: Are all these XMLs of the same structure? if not you will need some kind of filtering.
is the XML in your table's column a native XML-type already? Your example uses CONVERT extensivly... You will need a native XML in order to use .nodes()
If there's no native XML: Do you have to deal with invalid / uncastable data?
Are there rows with no data but you want to see them anyway? In this case you can try OUTER APPLY instead of CROSS APPLY.
For demonstration a running stand-alone mockup:
DECLARE #mockup TABLE(ID INT IDENTITY, [XML_FILE] XML);
INSERT INTO #mockup VALUES('<Example><File><SomeNode>blah</SomeNode><SomeOtherNode VAL="1"/></File></Example>')
,('<Example><File><SomeNode>blub</SomeNode><SomeOtherNode VAL="2"/></File></Example>')
SELECT
ID,
b.value('(SomeNode/text())[1]','nvarchar(100)') as [Some_Text],
b.value('(SomeOtherNode/#VAL)[1]','int') as [Some_Val]
FROM #mockup
CROSS APPLY [XML_FILE].nodes('Example/File') as a(b)

XML column Data into rows

I have a requirement where I have XML data column in database, which I need to pull in the form of values from rows XML data column from database. My XML is like
<ListID><ID>169346</ID><ID>289492</ID><ID>315264</ID><ID>415265</ID></ListID>
<ListID><ID>169356</ID><ID>299492</ID><ID>315264</ID><ID>415265</ID></ListID>
And I want data to be pulled up like
ID
169346
289492
315264
415265
169356
299492
315264
415265
You can use something like this:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY XmlColumn.nodes('//ID') AS XT(XC)
This basically takes every <ID> element that exists in the XML column, and extract the values as int and shows them in a result set.
Update: from your question, mentioning I have XML data column, I assumed that your column in the SQL Server table is in fact of type XML. And it should be, if you're storing XML in it!
But if it's not - then you need to cast your column to XML first, before using the function .nodes() in my code sample:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY CAST(YourColumn AS XML).nodes('//ID') AS XT(XC)
Here is one other way is to convert it to XML & then convert it to row :
SELECT split.a.value('.', 'varchar(max)') ID
FROM
(
SELECT CAST(ID AS XML) AS String from <table_name>
) a
CROSS APPLY String.nodes('//ID') AS split(a);
Result :
ID
169346
289492
315264
415265
169356
299492
315264
415265

Shred XML For Each Row in SQL Table

I have a table that contains two columns, and ID, and XML data. I'd like to shred the XML for each ID. I'm pulling out a single value within the XML and all the XML is structured the same I'm just not sure how to loop through the table and apply XML query to each row.
The query I need to apply is as follows:
Select top 1
Element1 = XV.value('(.)[1]','nvarchar(32)')
from #xml.nodes('Parameters/Parameter/Value') as x(XV)
So the end results would have two columns, ID and shredded value from XML.
Without any knowledge about your actual XML and how you want to shred it to get some values it is impossible to answer in completness, but this shoudl point you in the right direction:
Returns the ID and the XML as is
SELECT ID
,TheXmlColumn
FROM YourTable
This returns the ID and a value out of your XML
SELECT ID
,TheXmlColumn.value('Some XPaht','SomeType') AS SomeValueFromXML
FROM YourTable
And if there are more embedded rows it would be something like this
SELECT ID
,nd.value('Some XPaht','SomeType') AS SomeValueFromXMLRow
FROM YourTable
OUTER APPLY TheXmlColumn.nodes('SomeXPath') AS A(nd)
My magic glass bulb tells me, that you might need something like this:
SELECT ID
,TheXmlColumn.value('(Parameters/Parameter/Value)[1]','nvarchar(max)') AS SomeValueFromXML
FROM YourTable

Creating a lookup table from a column in table

I have a table in SQL Server that contains an ID and also a column with multiple values separated by a comma (like the example below)
ID Category_number
-------------------------
1 3,5,6,8
2 4,8,23
3 4,7,5,3
I need to make this into a lookup table with 1 category number per row, so like below;
ID Category_Number
-------------------------
1 3
1 5
1 6
I have been told that XPATH might be the solution to this. Does anyone have any sample code that will do this?
Thanks
See this answer Split
Create that function in your database.
Then you can create the results you want using:
SELECT
ID,
A.S Category_Number
FROM
MyCsvTable
CROSS APPLY dbo.Split (',', MyCsvTable.Category_Number) A
I know you just accepted the solution, but assuming you're using SQL Server, here is an alternative approach without building a function:
SELECT A.[id],
Split.a.value('.', 'VARCHAR(100)') AS Cat
FROM
(SELECT [id],
CAST ('<M>' + REPLACE(Cat, ',', '</M><M>') + '</M>' AS XML) AS String
FROM YourTable
) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
And some Fiddle: http://sqlfiddle.com/#!3/cf427/3
Best of luck!
You may also want to check HierarchyID in SQL SERVER. Some tutorial: http://www.codeproject.com/Articles/37171/HierarchyID-Data-Type-in-SQL-Server-2008
Basically all you need to do is to loop through string and find position of commas and replace them with chr(13) or such. In Oracle it can be done easily in a single query. I think the same can be done using HierarchyID in SQL Server starting from 2008 or maybe even earlier versions.

Compare Xml data in SQL

I have two tables with same NVARCHAR field that really contains XML data.
in some cases this really-XML-field is really same as one row in other table but differs in attributes order and therefor string comparison does not return the correct result!!!
and to determining the same XML fields ,I need to have a comparison like:
cast('<root><book b="" c="" a=""/></root>' as XML)
= cast('<root><book a="" b="" c=""/></root>' as XML)
but I get this Err Msg:
The XML data type cannot be compared or sorted, except when using the
IS NULL operator.
then what is the best solution to determine the same XML without re-casting them to NVARCHAR?
Why cast it at all? Just plug them into an XML column in a temp table and run Xquery to compare them to the other table. EDIT: Included example of the comparison. There are many, many ways to run the query against the XML to get the rows that are the same - exactly how that query is written is going to depend on preference, requirements, etc. I went with a simple group by/count, but a self join could be used, WHERE EXISTS against the columns that are being searched for duplicates, you name it.
CREATE TABLE #Test (SomeXML NVARCHAR(MAX))
CREATE TABLE #XML (SomeXML XML)
INSERT #Test (SomeXML)
VALUES('<root><book b="b" c="c" a="a"/></root>')
,('<root><book a="a" b="b" c="c"/></root>')
INSERT #XML (SomeXML)
SELECT SomeXML FROM #Test;
WITH XMLCompare (a,b,c)
AS
(
SELECT
x.c.value('#a[1]','char(1)') AS a
,x.c.value('#b[1]','char(1)') AS b
,x.c.value('#c[1]','char(1)') AS c
FROM #XML
CROSS APPLY SomeXMl.nodes('/root/book') X(C)
)
SELECT
a
,b
,c
FROM XMLCompare as a
GROUP BY
a
,b
,c
HAVING COUNT(*) >1