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;
Related
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
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'
In my SQL Server DB I have table with an XML column. The XML that goes in it is like the sample below:
<Rows>
<Row>
<Name>John</Name>
</Row>
<Row>
<Name>Debbie</Name>
</Row>
<Row>
<Name>Annie</Name>
</Row>
<Row>
<Name>John</Name>
</Row>
</Rows>
I have a requirement that I need to find the occurrence of all rows where the XML data has duplicate entries of <Name>. For example, above we have 'John' twice in the XML.
I can use the exist XML statement to find 1 occurrence, but how can I find if it's more than 1? Thanks.
To identify any table row that has duplicate <Name> values in its XML, you can use exist as well:
exist('//Name[. = preceding::Name]')
To identify which names are duplicates, respectively, you need nodes and CROSS APPLY
SELECT
t.id,
x.Name.value('.', 'varchar(100)') AS DuplicateName
FROM
MyTable t
CROSS APPLY t.MyXmlColumn.nodes('//Name[. = preceding::Name]') AS x(Name)
WHERE
t.MyXmlColumn.exist('//Name[. = preceding::Name]')
Try this:
;with cte as
(SELECT tbl.col.value('.[1]', 'varchar(100)') as name
FROM yourtable
CROSS APPLY xmlcol.nodes('/Rows/Row/Name') as tbl(col))
select name
from cte
group by name
having count(name) > 1
We first use the nodes function to convert from XML to relational data, then use value to get the text inside the Name node. We then put the result of the previous step into a CTE, and use a simple group by to get the value with multiple occurences.
Demo
I'm trying to generate an XML output from SQL and need to use a UNION statement and also name the output column.
I had this working before when I didn't need to use a UNION statement using:
select(
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAccs] FOR XML PATH ('AccountDetails'), root ('Root')
) as XmlOutput
Which named the output XML column as XmlOutput
I am now trying:
select(
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAccs]
UNION
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAppAccs]
FOR XML PATH ('AccountDetails'), root ('Root')
) as XmlOutput
But receive an error message, does anyone know a way around this?
The FOR XML clause is invalid in views, inline functions, derived tables, and subqueries when they contain a set operator. To work around, wrap the SELECT containing a set operator using derived table syntax and apply FOR XML on top of it.
Thanks
J.
Wrap your 2 selects on a single one like so:
select (
select id, name from (
select id, name
from xmltest
UNION
select id, name
from xmltest
) A
FOR XML PATH ('AccountDetails'), root ('Root')
) As XmlOutput
I have some XML data stored in a varchar(max) column on SQL Server 2005. The data is in the form (FQTN = fully qualified type name):
<?xml version="1.0" encoding="utf-16"?>
<History xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EntityViews>
<EntityProxy Type="FQTN" Key="386876" />
<EntityProxy Type="FQTN" Key="387981" />
<!-- etc. -->
</EntityViews>
</History>
How can I select Type, Key so that I get a tabular result from the XML data in this column for a single row? The table has an identity primary key named HistoryId.
;with cteCastToXML as (
select CAST(YourColumn as xml) as x
from YourTable
)
select h.ep.value('#Type','varchar(10)') as [Type],
h.ep.value('#Key', 'varchar(10)') as [Key]
from cteCastToXML
cross apply x.nodes('/History/EntityViews/EntityProxy') as h(ep)
My recommendation would be two fold.
If this is what you will be doing with the column, change the column to be an XML column.
If you need to do this one time, look at taking the value and converting it to XML, then you can operate on it like you would normally. (Here is a link on how to convert).