Extract data XML columns in SQL Server - sql-server

I have a table with 70K records with a column XMLMetadata - that column holds all the 70k xml data.
I need a way to extra a item from the xml columns for all 70K records. The item name that I need to pull from all 70k is <Item Name="DocTitle" Type="String">.
Is there a way I can easily pull this?
<Metadata>
<Item Name="ID" Type="String">1364416</Item>
<Item Name="Name" Type="String">website</Item>
<Item Name="Type" Type="String">WebContent</Item>
<Item Name="Title" Type="String">Close Out Letter 11/1/17</Item>
<Item Name="Author" Type="String">Seba</Item>
....
</Metadata>

Try this query
SELECT
XMLMetadata.value('(/Metadata/node())[1]', 'nvarchar(max)') as ID,
XMLMetadata.value('(/Metadata/node())[2]', 'nvarchar(max)') as Name,
XMLMetadata.value('(/Metadata/node())[3]', 'nvarchar(max)') as Type,
XMLMetadata.value('(/Metadata/node())[4]', 'nvarchar(max)') as Title,
XMLMetadata.value('(/Metadata/node())[5]', 'nvarchar(max)') as Author
FROM [myTable]

If you want to get all items with the name, type and value, you could use something like this:
SELECT
ItemName = XC.value('(#Name)', 'varchar(20)'),
ItemType = XC.value('(#Type)', 'varchar(20)'),
ItemValue = XC.value('(.)', 'varchar(50)')
FROM
dbo.YourTableNameHere
CROSS APPLY
XmlMetadata.nodes('/Metadata/Item') AS XT(XC)
and if you want to get just a single value, based on the Name attribute, you could use this code here:
SELECT
ItemValue = XmlMetadata.value('(/Metadata/Item[#Name="Title"]/text())[1]', 'varchar(50)')
FROM
dbo.YourTableNameHere

Related

Unable to save data from xml format in SQL table

SELECT *
FROM #myHierarchy
FOR XML AUTO
Data is
<_x0040_myHierarchy element_id="1" parent_ID="1" NAME="itemCode" StringValue="Simmi" ValueType="string" />
I'm unable to load data in this query
SELECT #xml = dbo.ToXML(#myHierarchy);
SELECT
a.b.value('#ItemCode', 'varchar(20)') AS ItemCode
FROM
#xml.nodes('/root/_x0040_myHierarchy') a(b)
In this query, itemcode is blank. How can I load data using this query?
Your sample XML does not contain any attribute ItemCode - it has these attributes:
element_id
parent_ID
NAME
StringValue
ValueType
So which value do you really want to read out from the XML element?
Update: to retrieve the StringValue attribute, use this code:
SELECT
XC.value('#StringValue', 'varchar(50)')
FROM
#xml.nodes('/_x0040_myHierarchy') AS XT(XC)
If your XML contains a <root> ..... </root> root element, and multiple <_x0040_myHierarchy> elements inside, and you want to extract the one with #Name = 'itemCode' - then you need to use this SELECT:
SELECT
XC.value('#StringValue', 'varchar(50)')
FROM
#xml.nodes('/root/_x0040_myHierarchy') AS XT(XC)
WHERE
XC.value('#NAME', 'varchar(50)') = 'itemCode'

SQL Server query xml column

I need to pull values from an XML column. The table contains 3 fields with one being an XML column like below:
TransID int,
Place varchar(20),
Custom XML
The XML column is structured as following:
<Fields>
<Field>
<Id>9346-00155D1C204E</Id>
<TransactionCode>0710</TransactionCode>
<Amount>5.0000</Amount>
</Field>
<Field>
<Id>A6F0-BA07EF3A7D43</Id>
<TransactionCode>0885</TransactionCode>
<Amount>57.9000</Amount>
</Field>
<Field>
<Id>9BDA-7858FD182Z3C</Id>
<TransactionCode>0935</TransactionCode>
<Amount>25.85000</Amount>
</Field>
</Fields>
I need to be able to query the xml column and return only the value for the <Amount> if there is a <Transaction code> = 0935. Note: there are records where this transaction code isn’t present, but it won't exist in the same record twice.
This is probably simple, but I’m having a problem returning just the <amount> value where the <transaction code> = 0935.
You can try this way :
DECLARE #transCode VARCHAR(10) = '0935'
SELECT field.value('Amount[1]', 'decimal(18,5)') as Amount
FROM yourTable t
OUTER APPLY t.Custom.nodes('/Fields/Field[TransactionCode=sql:variable("#transCode)"]') as x(field)
Alternatively, you can put logic for filtering Field by TransactionCode in SQL WHERE clause instead of in XPath expression, like so :
DECLARE #transCode VARCHAR(10) = '0935'
SELECT field.value('Amount[1]', 'decimal(18,5)') as Amount
FROM yourTable t
OUTER APPLY t.Custom.nodes('/Fields/Field') as x(field)
WHERE field.value('TransactionCode[1]', 'varchar(10)') = #transCode
SQL Fiddle Demo
You can use an XPath like this in your TSQL:
SELECT
*,
Custom.value('(/Fields/Field[#Name="Id"]/#Value)[1]', 'varchar(50)')
FROM YourTable
WHERE Custom.value('(/Fields/Field[#Name="Id"]/#Value)[1]', 'varchar(50)') = '0655'

SQL Server SELECT FOR XML How to get two elements at the same level

I have two different pieces of XML to put together.
For example, SQL for the first piece looks like this:
SELECT
*
FROM
(
SELECT 1 AS OrdNum, 'Abc' AS Name
) a
FOR XML
AUTO,
TYPE
Once executed, you'll get this:
<a OrdNum="1" Name="Abc" />
The second one is here:
SELECT
*
FROM
(
SELECT 4 AS Age, 'M' AS Sex, 'John' AS FirstName
) b
FOR XML
AUTO,
TYPE
You'll get this:
<b Age="4" Sex="M" FirstName="John" />
Now I'll put the two pieces together:
SELECT
*
FROM
(
SELECT
(
SELECT
*
FROM
(
SELECT 1 AS OrdNum, 'Abc' AS Name
) a
FOR XML
AUTO,
TYPE
) AS aa
,
(
SELECT
*
FROM
(
SELECT 4 AS Age, 'M' AS Sex, 'John' AS FirstName
) b
FOR XML
AUTO,
TYPE
) AS bb
) Data
FOR XML
AUTO,
ELEMENTS
The result is as follows:
<Data>
<aa>
<a OrdNum="1" Name="Abc" />
</aa>
<bb>
<b Age="4" Sex="M" FirstName="John" />
</bb>
</Data>
But I do not want to have the elements "aa" and "bb" there. I'd love to get this:
<Data>
<a OrdNum="1" Name="Abc" />
<b Age="4" Sex="M" FirstName="John" />
</Data>
But I have no idea how to achieve that.
Any hints?
There is no "simple" way to do it. FOR XML PATH|EXPLICIT|AUTO will all require each top-level, output element to have the same name. And you can't UNION multiple FOR XML queries together (Sql Server 2012).
The direction you went in is the most reliable and flexible. Essentially, you have to add a separate column for each different element type you want to include. You could simplify your final attempt to this to get what you wanted:
SELECT
(
SELECT 1 AS [#OrdNum], 'Abc' AS [#Name]
WHERE 1=1
FOR XML PATH ('a'), TYPE
)
,
(
SELECT 4 AS [#Age], 'M' AS [#Sex], 'John' AS [#FirstName]
WHERE 1=1
FOR XML PATH ('b'), TYPE
)
FOR XML PATH ('Data'), TYPE;
The above query outputs:
<Data>
<a OrdNum="1" Name="Abc" />
<b Age="4" Sex="M" FirstName="John" />
</Data>
When you use FOR XML PATH, the column aliases are XPaths. So to make it an attribute name, you have to prefix with '#'---which then requires you to escape the alias (hence the []). The parameter on PATH dictates the name of each row's Xml element. The TYPE option says to keep the output as type Xml instead of nvarchar(max), which means that the outer query can merge it better. And the outer query just has 2 columns to stuff into the single element it represents. Finally, I like the WHERE 1=1, but it's not syntactically required.
A tangent: I know your example is simplified, so you may wish to know that Xml data types can have "methods" applied to them. For example, say you wanted the above, but an outer query only needed the "b" elements. You could use the query() method to select only parts of the Xml to merge into some outer query.
SELECT
(
SELECT
(
SELECT 1 AS [#OrdNum], 'Abc' AS [#Name]
WHERE 1=1
FOR XML PATH('a'), TYPE
)
,
(
SELECT 4 AS [#Age], 'M' AS [#Sex], 'John' AS [#FirstName]
WHERE 1=1
FOR XML PATH('b'), TYPE
)
FOR XML PATH('Data'), TYPE
).query('Data/b');
Which produces this:
<b Age="4" Sex="M" FirstName="John" />
You need to look at the FOR XML PATH option that SQL Server 2005 introduced - see the What's New in FOR XML in Microsoft SQL Server 2005 document for more information.
Basically, with FOR XML PATH, you can define the shape of your XML very easily. You can define certain structures, you can define certain columns to be output as attributes, and others as elements - totally under your control.
You can get more information on how to format that here:
enter link description here

Shredding XML with unknown schema

I have to shred XML with unknown schema to a table. I don't know what elements there are in XML. And I am also not aware of XML format. In some cases XML data is Attribute centric and in some cases it is element centric.
For Example -
I have two XML -
<Root>
<Recorset>
<RecordsetId>1</RecordsetId>
<RecordsetName>name1</RecordsetName>
</Recorset>
</Root>
AND
<Root>
<Recorset RecordsetId="2" RecordsetName="name2"></Recorset>
</Root>
XML can have any other element/attributes. I need to capture the elements/attributes name and respective data using EDGE table produced from OPENXML.
What is the simplest way of doing this?
I need output in given format -
RecodrsetId RecordsetName
1 Name1
2 Name2
something like this?
select
C.Name,
C.Value
from #Data.nodes('//*') as T(C)
outer apply (
select
T.C.value('local-name(.)', 'nvarchar(max)') as Name,
T.C.value('(./text())[1]', 'nvarchar(max)') as Value
union all
select
A.C.value('local-name(.)', 'nvarchar(max)') as Name,
A.C.value('.', 'nvarchar(max)') as Value
from T.C.nodes('#*') as A(C)
) as C
where C.Value is not null
sql fiddle demo

find all duplicate values in an element attribute

I have an xml document that is stored in sql server 2005 that I want to find all duplicate values from an id attribute within an xml element. The xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<session sessionValue="" id="ID-1">
<data id="ID-3">
<id>d394E6FF844734CB9A5E8B17612DC050A</id>
<TotalResult>803</TotalResult>
<AgencyListRequestURL>http://ews.harleysvillegroup.com:5555/invoke/HmiPortalDCTRedirect.flows:getAgencyList</AgencyListRequestURL>
<system id="systemid">
<global id="ID-2">
<id>gEBE0E6C2D61340698B264E30D1B2DC59</id>
<Button />
<GlobalOutputSpacer />
<Spacer />
<LocationIDToCompare />
<MasterManuScriptID />
<CurrentVehicle />
</global>
<page id="ID-2">
<id>p7B81433D8EB4400CA775CB3F7F0AD4DE</id>
<DialogMode>0</DialogMode>
<DifferentAddress>0</DifferentAddress>
</page>
</system>
</data>
</session>
In this example I am looking to generate a sql result that looks in all the “ID” attributes in the entire XML document and will tell me that “ID-2” is a duplicate value.
Is this possible in XPATH?
Thanks for your help!
select id, count(*)
from (
select x.value('#id', 'varchar(100)') as id
from #x.nodes('//*[#id]') t(x)) as ids
group by id
having count(*) > 1;
The key is the XPath //*[#id] that selects all document elements with an id attribute.
Update
If the XML is in a table field:
select id, count(*)
from (
select x.value('#id', 'varchar(100)') as id
from [<table>]
cross apply [<table>].[<field>].nodes('//*[#id]') t(x)) as ids
group by id
having count(*) > 1;
This will select duplicate values across all rows. If you want to select duplicate attribute values only within each row, add the table primary key to the group by:
select id, <primary key>, count(*)
from (
select x.value('#id', 'varchar(100)') as id
, <primary key>
from [<table>]
cross apply [<table>].[<field>].nodes('//*[#id]') t(x)) as ids
group by id, <primary key>
having count(*) > 1;

Resources