Stored procedure Read xml file with namespaces - sql-server

I am trying to read the following xml using a stored procedure in SQL server 2016.
DECLARE #xml as xml
SET #xml = '<ns:MT_FlexParams_SPICE xmlns:ns="urn:ptl.com:pi:imdl">
<row>
<ACCT>100043</ACCT>
<BROL>10085437</BROLE>
<STRTDAT>2019-11-01 00:00:00.0</STRTDAT>
<ENDDAT>2021-05-15 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE> </DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10126</IRQID>
<BRAUT_GRA> </BRAUT_GRA>
<BRAUT_REV> </BRAUT_REV>
</row>
<row>
<ACCT>100037</ACCT>
<BROL>10085437</BROLE>
<STRTDAT>2019-10-21 00:00:00.0</STRTDAT>
<ENDDAT>2020-10-21 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE> </DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10106</IRQID>
<BRAUT_GRA> </BRAUT_GRA>
<BRAUT_REV> </BRAUT_REV>
</row>
</ns:MT_FlexParams_SPICE>'
WITH XMLNAMESPACES (DEFAULT 'urn:ptl.com:pi:imdl')
SELECT #xml.value('(row/ACCT)[1]', 'nvarchar(100)') as ACCT;
But it is fetching null result. Could you please let me know the mistake that I am performing?

The XML is not well-formed, so I had to fix it.
You need to use .nodes() method to shred the XML into a rectangular structure.
SQL
DECLARE #xml AS XML = N'<ns:MT_FlexParams_SPICE xmlns:ns="urn:ptl.com:pi:imdl">
<row>
<ACCT>100043</ACCT>
<BROL>10085437</BROL>
<STRTDAT>2019-11-01 00:00:00.0</STRTDAT>
<ENDDAT>2021-05-15 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE>
</DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10126</IRQID>
<BRAUT_GRA>
</BRAUT_GRA>
<BRAUT_REV>
</BRAUT_REV>
</row>
<row>
<ACCT>100037</ACCT>
<BROL>10085437</BROL>
<STRTDAT>2019-10-21 00:00:00.0</STRTDAT>
<ENDDAT>2020-10-21 00:00:00.0</ENDDAT>
<DELDATE>1900-01-01 00:00:00.0</DELDATE>
<DEF_ROLE>
</DEF_ROLE>
<BRASTA>10</BRASTA>
<IRQID>10106</IRQID>
<BRAUT_GRA>
</BRAUT_GRA>
<BRAUT_REV>
</BRAUT_REV>
</row>
</ns:MT_FlexParams_SPICE>';
;WITH XMLNAMESPACES ('urn:ptl.com:pi:imdl' AS ns)
SELECT c.value('(ACCT/text())[1]', 'VARCHAR(100)') as ACCT
, c.value('(BROL/text())[1]', 'VARCHAR(100)') as BROL
FROM #xml.nodes('/ns:MT_FlexParams_SPICE/row') AS t(c);
Output
+--------+----------+
| ACCT | BROL |
+--------+----------+
| 100043 | 10085437 |
| 100037 | 10085437 |
+--------+----------+

Related

multiple nodes with same name as rows

This may look like a duplicate, but what I can find are getting multiple rows from nodes with elements inside, like
<products>
<product>
<image>url1</image>
</product>
<product>
<image>url1</image>
</product>
</products>
What I have is an XML-field in a table (with PLU as an integer)
<product>
<images>
<image>url1</image>
<image>url2</image>
<image>url3</image>
</images>
</product>
I want
image
-----
url1
url2
url3
I tried
select a.image.value('image','nvarchar(max)') as image
from products r
cross apply r.xml.nodes('/product/images') a(image) where PLU='8019'
but that gives
XQuery [products.xml.value()]: 'value()' requires a singleton (or empty sequence),
found operand of type 'xdt:untypedAtomic *'
As I want the value of each node, not of subnodes, I tried
select a.image.value('.','nvarchar(max)') ...
but that gave me only one row with url1url2url3 all urls concatenated.
select a.image.value('image[1]','nvarchar(max)')
gives only url1
PLease try the following solution.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT #tbl (xmldata) VALUES
(N'<product>
<images>
<image>url1</image>
<image>url2</image>
<image>url3</image>
</images>
</product>');
-- DDL and sample data population, end
SELECT ID
, c.value('text()[1]','nvarchar(max)') AS image_url
FROM #tbl
CROSS APPLY xmldata.nodes('/product/images/image') AS t(c);
Output
+----+-----------+
| ID | image_url |
+----+-----------+
| 1 | url1 |
| 1 | url2 |
| 1 | url3 |
+----+-----------+
A shorter solution than Yitzhak Khabhinsky is this by Martin Boje Carpentier elsewhere, but I'll award the points to Yitzhak
select a.image.value('.','nvarchar(max)') as image
from products r
cross apply r.xml.nodes('/product/images/*') a(image) where PLU='8019'

Can SQL Server look ahead into XML tags and/or count them?

I have XML that looks like this:
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png" />
</channel>
Note the 5 XML elements all named display-name.
Here is the SQL Server code to insert the data:
SELECT *
INTO channels
FROM OPENXML (#hdoc, '/tv/channel', 2)
WITH (
id varchar(50) '#id',
chan1 varchar(50) 'display-name[1]',
chan2 varchar(50) 'display-name[2]',
chan3 varchar(50) 'display-name[3]',
chan4 float 'display-name[4]',
chan5 varchar(50) 'display-name[5]',
icon varchar(100)
)
The values of each tag is the same for all occurrences of the <channel> element, so use of the data in the database is not affected.
However, here is the XML in a new upgrade:
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png" />
</channel>
Note that the 2nd XML element is removed (presumably because it's identical to the 1st one). But when they are different, both elements appear.
So now I can't tell what's in the subsequent elements.
My question is, can SQL Server count the display-name elements, or look at and test the data values in the elements?
I tried this test and it works:
select iif(isnull(null, -1) = -1, 'null', 'not null')
But this code:
chan2 varchar(50) iif(isnull('display-name[5]', -1) = -1, 'display-name[2]', 'display-name[1]'),
Gives a syntax error.
If this can work, then the same format will be used for the columns chan2 through chan5. E.g, chan3 will be assigned to either index 3 or 2 of display-name, etc.
Suggestions?
Regardless of which xml format is encountered (whether there are 4 or 5 display-name elements), the result should look like this:
id chan1 chan2 chan3 chan4 chan5
EPG123.11331.schedulesdirect.org WCBS WCBS 2 WCBS 2 CBS
There will be cases where chan1 is different from chan2. But I can't tell when that will happen.
Here is the code that is causing a syntax error in the INTO clause.
DECLARE #x xml
SELECT #x = P
FROM OPENROWSET (BULK 'C:\projects\epg123new.xml', SINGLE_BLOB) AS tv(P)
DECLARE #hdoc int
EXEC sp_xml_preparedocument #hdoc OUTPUT, #x
insert into channels (id, chan1, chan2, chan3, chan4, chan5, icon)
SELECT
channel.value('(#id)[1]', 'varchar(50)') id,
channel.value('(display-name)[1]','varchar(50)') chan1,
channel.value('(display-name[(if(count(../display-name) = 4) then 1 else 2)])[1]','varchar(50)') chan2,
channel.value('(display-name[(if(count(../display-name) = 4) then 2 else 3)])[1]','varchar(50)') chan3 ,
channel.value('(display-name[(if(count(../display-name) = 4) then 3 else 4)])[1]','float') chan4,
channel.value('(display-name[(if(count(../display-name) = 4) then 4 else 5)])[1]', 'varchar(50)') chan5,
channel.value('(icon/#src)[1]','varchar(100)') icon
INTO channels
FROM #hdoc.nodes('/channel') x(channel)
EXEC sp_xml_removedocument #hdoc
go
Another method. Slightly more performant as it calculates number of <display-name> elements just once per channel. And eliminates a conditional logic with the if/else statement.
All credit goes to #Charlieface. I just optimized his solution.
SQL
DECLARE #xml XML =
N'<tv>
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png"/>
</channel>
<channel id="EPG123.11331.schedulesdirect.org">
<display-name>WCBS</display-name>
<display-name>2 WCBS</display-name>
<display-name>2</display-name>
<display-name>CBS</display-name>
<icon src="https://schedulesdirect-api20141201.s3. ... w_270h.png"/>
</channel>
</tv>';
--INSERT INTO channels (...)
SELECT
c.value('(#id)[1]', 'varchar(50)') id,
c.value('(display-name/text())[1]','varchar(50)') chan1,
c.value('(display-name[sql:column("seq.pos") - 3]/text())[1]','varchar(50)') chan2,
c.value('(display-name[sql:column("seq.pos") - 2]/text())[1]','varchar(50)') chan3 ,
c.value('(display-name[sql:column("seq.pos") - 1]/text())[1]','varchar(50)') chan4,
c.value('(display-name[sql:column("seq.pos")]/text())[1]', 'varchar(50)') chan5,
c.value('(icon/#src)[1]','varchar(100)') icon
--, seq.pos
FROM #xml.nodes('/tv/channel') AS t(c)
CROSS APPLY (SELECT t.c.value('count(./display-name)','INT') AS pos
) AS seq;
Output
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
| id | chan1 | chan2 | chan3 | chan4 | chan5 | icon |
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
| EPG123.11331.schedulesdirect.org | WCBS | WCBS | 2 WCBS | 2 | CBS | https://schedulesdirect-api20141201.s3. ... w_270h.png |
| EPG123.11331.schedulesdirect.org | WCBS | WCBS | 2 WCBS | 2 | CBS | https://schedulesdirect-api20141201.s3. ... w_270h.png |
+----------------------------------+-------+-------+--------+-------+-------+--------------------------------------------------------+
OPENXML has been somewhat deprecated in favour of XQuery functions such as .nodes() and .value().
In this example, I have used the variable directly in the FROM. If you have an XML table column, you should use CROSS APPLY.
.nodes breaks out the XML into rows, the example you gave does not have tv as the root node.
.value requires a single value result, best practice is to enclose the whole XQuery in brackets, followed by [1] to get the first node.
text() gets you the actual value of a node, you don't technically need this, but it performs faster.
# is short-hand to refer to an attribute.
.. is short-hand to refer to the immediate parent node.
We use the predicate [(if(count(../display-name) = 4) then ... to get the particular node we want, we still need [1] because it is not a constant expression.
SELECT
channel.value('(#id)[1]', 'varchar(50)') id,
channel.value('(display-name/text())[1]','varchar(50)') chan1,
channel.value('(display-name[(if(count(../display-name) = 4) then 1 else 2)]/text())[1]','varchar(50)') chan2,
channel.value('(display-name[(if(count(../display-name) = 4) then 2 else 3)]/text())[1]','varchar(50)') chan3 ,
channel.value('(display-name[(if(count(../display-name) = 4) then 3 else 4)]/text())[1]','float') chan4,
channel.value('(display-name[(if(count(../display-name) = 4) then 4 else 5)]/text())[1]', 'varchar(50)') chan5,
channel.value('(icon/#src)[1]','varchar(100)') icon
INTO channels
FROM #hdoc.nodes('/channel') x(channel)

SQL SERVER XQuery node processing problem

I have to process data from xml by date of operation, simulating day by day, during 4th months process in the data base with a XML file that looks like this:
<Operaciones_por_Dia>
<OperacionDia fecha="2020-01-30">
<PagoRecibo TipoRecibo="5" NumFinca="9782331"/>
<PagoRecibo TipoRecibo="5" NumFinca="6696849"/>
<TransConsumo id="1" LecturaM3="325" descripcion="Cobro Mensual" NumFinca="3336538"/>
<TransConsumo id="3" LecturaM3="40" descripcion="Lectura errĂ³nea" NumFinca="2425954"/>
</OperacionDia>
<OperacionDia fecha="2020-04-08">
<PagoRecibo TipoRecibo="7" NumFinca="1423800"/>
<PagoRecibo TipoRecibo="7" NumFinca="1393022"/>
<TransConsumo id="2" LecturaM3="22" descripcion="Reclamo de cliente" NumFinca="2101885"/>
</OperacionDia>
</Operaciones_por_Dia>
When I prepare the data, I store all the dates from XML into a variable table and then I iterate from the min date throught the max date (#FechaOperacion has stored that date), then I have to process the node data where the date match with the date from xml node .
I try it doing this, but #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE')
don't match well, the correct data that I have to compare is Fecha with #FechaOperacion, but I don't know how can I get that value with Xquery.
INSERT #PagosHoy (NumFinca, TipoRecibo, Fecha)
select ph.value('#NumFinca', 'INT')
, ph.value('#TipoRecibo', 'INT')
, ph.value('../#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') AS t(ph)
where #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE') = #FechaOperacion
With the following code it work well, but I want to know how to do it like the other way.
INSERT INTO #PagosHoy(NumFinca,TipoRecibo,Fecha)
SELECT [NumFinca],[TipoRecibo],[fechaDeIngreso]
FROM OPENXML (#hdoc, 'Operaciones_por_Dia/OperacionDia/PagoRecibo',1)
WITH ( [NumFinca] VARCHAR(30) '#NumFinca',
[TipoRecibo] INT '#TipoRecibo',
[fechaDeIngreso] VARCHAR(100) '../#fecha')
WHERE [fechaDeIngreso] = #FechaOperacion
EXEC spProcesaPagos #PagosHoy
Microsoft proprietary OPENXML and its companions sp_xml_preparedocument and sp_xml_removedocument are kept just for backward compatibility with the obsolete SQL Server 2000. It is strongly recommended to re-write your SQL and switch it to XQuery.
Here is another method similar to what is proposed by #AlwaysLearning, but more simple.
It is using XPath predicate instead of WHERE clause.
SQL
-- DDL and sample data population, start
DECLARE #PagosHoy TABLE (ID INT IDENTITY PRIMARY KEY, NumFinca INT, TipoRecibo INT, Fecha DATE);
DECLARE #xml XML =
N'<Operaciones_por_Dia>
<OperacionDia fecha="2020-01-30">
<PagoRecibo TipoRecibo="5" NumFinca="9782331"/>
<PagoRecibo TipoRecibo="5" NumFinca="6696849"/>
<TransConsumo id="1" LecturaM3="325" descripcion="Cobro Mensual"
NumFinca="3336538"/>
<TransConsumo id="3" LecturaM3="40" descripcion="Lectura errĂ³nea"
NumFinca="2425954"/>
</OperacionDia>
<OperacionDia fecha="2020-04-08">
<PagoRecibo TipoRecibo="7" NumFinca="1423800"/>
<PagoRecibo TipoRecibo="7" NumFinca="1393022"/>
<TransConsumo id="2" LecturaM3="22" descripcion="Reclamo de cliente"
NumFinca="2101885"/>
</OperacionDia>
</Operaciones_por_Dia>';
-- DDL and sample data population, end
DECLARE #FechaOperacion DATE = '2020-01-30';
INSERT #PagosHoy (NumFinca, TipoRecibo, Fecha)
SELECT c.value('#NumFinca', 'INT')
, c.value('#TipoRecibo', 'INT')
, #FechaOperacion AS FechaOperacion
FROM #xml.nodes('/Operaciones_por_Dia/OperacionDia[#fecha eq sql:variable("#FechaOperacion")]/PagoRecibo') AS t(c)
-- test
SELECT * FROM #PagosHoy;
Output
+----+----------+------------+------------+
| ID | NumFinca | TipoRecibo | Fecha |
+----+----------+------------+------------+
| 1 | 9782331 | 5 | 2020-01-30 |
| 2 | 6696849 | 5 | 2020-01-30 |
+----+----------+------------+------------+
The where clause in your first example queries /Operaciones_por_Dia/OperacionDia/#fecha[1] independently of the nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') query so will always be comparing #FechaOperacion against the first date attribute - which is 2020-01-30 in this document.
declare #FechaOperacion date = convert(date, '2020-01-30', 120);
select
ph.value('#NumFinca', 'INT'),
ph.value('#TipoRecibo', 'INT'),
ph.value('../#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia/PagoRecibo') AS t(ph)
where #DocumentoXML.value('(/Operaciones_por_Dia/OperacionDia/#fecha)[1]', 'DATE') = #FechaOperacion
Depending on the value of #FechaOperacion it will either return all rows in the document (when it matches) or now rows at all (when it doesn't)...
(No column name) (No column name) (No column name)
---------------- ---------------- ----------------
9782331 5 2020-01-30
6696849 5 2020-01-30
1423800 7 2020-04-08
1393022 7 2020-04-08
The solution is to query for nodes('/Operaciones_por_Dia/OperacionDia') and then use a cross apply to query the PagoRecibo child nodes.
declare #FechaOperacion date = convert(date, '2020-01-30', 120);
select
ph.value('#NumFinca', 'INT'),
ph.value('#TipoRecibo', 'INT'),
od.value('#fecha', 'DATE')
from #DocumentoXML.nodes('/Operaciones_por_Dia/OperacionDia') AS s(od)
cross apply s.od.nodes('PagoRecibo') AS t(ph)
where s.od.value('#fecha', 'DATE') = #FechaOperacion
Which returns...
(No column name) (No column name) (No column name)
---------------- ---------------- ----------------
9782331 5 2020-01-30
6696849 5 2020-01-30

Order by XML attribute value

I have the following table
EntityID | Data
-----------+-----------
1 | <xml data>
2 | <xml data>
3 | <xml data>
Where XML DATA looks like this:
<Qualifications>
<Qualification QualificationName="Has Dr's degree" Remark="Yes" />
<Qualification QualificationName="ASP.NET Experience" Remark="1 Year" />
<Qualification QualificationName="Sex" Remark="M" />
</Qualifications>
I'd like to have the ability to order by remark for a particular QualificationName
SELECT * FROM Table
....
ORDER BY 'ASP.NET Experience'
P.S.
Potencially I can change XML to something like this to make things simplier
<Qualifications>
<Has Dr's degree>Yes</Has Dr's degree>
<ASP.NET Experience>1 Year</ASP.NET Experience>
<Sex>M</Sex>
</Qualifications>
UPD1: For case when user wants to order by 'ASP.NET Experience' qualification The expected result would be like this:
EntityID | Data
-----------+-----------
3 | <xml data>
1 | <xml data>
2 | <xml data>
Because EntityID 3 has Remark '1 year' EntityID 1 has remark '2 years' and EntityID 2 has remark '3 years' inside XML column for 'ASP.NET Experience' qualification
Assuming #QualificationName identifies the particular node you want to order by this will give you the value of the Remark.
declare #xml xml; set #xml = '<Qualifications>
<Qualification QualificationName="Has Dr%quot;s degree" Remark="Yes" />
<Qualification QualificationName="ASP.NET Experience" Remark="1 Year" />
<Qualification QualificationName="Sex" Remark="M" />
</Qualifications>'
declare #Qualification nvarchar(100); set #Qualification = 'ASP.NET Experience'
select #xml.value('(//Qualifications/Qualification[#QualificationName=sql:variable("#Qualification")]/#Remark)[1]', 'varchar(10)')
I assumed your xml as:
<Qualifications>
<HasDrsdegree>Yes</HasDrsdegree>
<ASPNETExperience>2</ASPNETExperience>
<Sex>M</Sex>
</Qualifications>
I hope this query will resolve your problem, this query results one extra column which you can ignore but this will solve you problem for complete table in one go.
Select EntityID,Data,T2.Loc.value('.','int') as 'experience'
from [yourtablename]
cross apply Data.nodes('/Qualifications/ASPNETExperience') as T2(Loc)
order by experience

TSQL: Extract XML Nested Tags Into Columns

Using SQLServer2008R2
I currently have XML tags with data inside the XML tags (not between them), such as:
<zooid="1"><animals key="all" zebras="22" dogs="0" birds="4" /><animals key="all" workers="yes" vacation="occasion" /> ... *(more)*</zooid>
<zooid="2"><animals key="house" zebras="0" dogs="1" birds="2" /><animals key="house" workers="no" vacation="no" /> ... *(more)*</zoodid>
If I query the XML or use the value function against it, it returns blank values because it tries to read between tags - where no value exists. I need it to read inside of the tags, parse out the values before the equal sign as columns and the values between the quotations as values inside those columns (granted, I could create a function that could do this, but this would be quite meticulous, and I'm curious if something like this already exists). What it should look like this in columns:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
... and this in rows of data:
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
So the final output (just using the two XML rows from the beginning for now), would look like the below data in table form:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
================================================================
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
Other than querying against XML, using the .query tool and even trying the .node tool (using CROSS APPLY see this thread), I haven't been able to generate this.
Try this one -
DECLARE #YourXML NVARCHAR(MAX)
SELECT #YourXML = '
<zooid="1">
<animals key="all" zebras="22" dogs="0" birds="4" />
<animals key="all" workers="yes" vacation="occasion" />
</zooid>
<zooid="2">
<animals key="house" zebras="0" dogs="1" birds="2" />
<animals key="house" workers="no" vacation="no" />
</zoodid>'
DECLARE #XML XML
SELECT #XML =
REPLACE(
REPLACE(#YourXML, 'zooid=', 'zooid id=')
, '</zoodid>'
, '</zooid>')
SELECT
d.[Key]
, Dogs = MAX(d.Dogs)
, Zebras = MAX(d.Zebras)
, Birds = MAX(d.Birds)
, Workers = MAX(d.Workers)
, Vacation = MAX(d.Vacation)
FROM (
SELECT
[Key] = t.p.value('./#key', 'NVARCHAR(50)')
, Zebras = t.p.value('./#zebras', 'INT')
, Dogs = t.p.value('./#dogs', 'INT')
, Birds = t.p.value('./#birds', 'INT')
, Workers = t.p.value('./#workers', 'NVARCHAR(20)')
, Vacation = t.p.value('./#vacation', 'NVARCHAR(20)')
FROM #XML.nodes('/zooid/animals') t(p)
) d
GROUP BY d.[Key]
Your xml appears invalid. How are you able to specify an element like this: ? Generally xml structure is <(elementName) (Attribute)="(Value)"/>. Unless I am mistaken if you are casting text to xml the way it is it will fail. Saying that I can show a working example for proper xml in a self extracting example that will run in SQL Managment Studio as is.
declare #text1 varchar(max) = '<zooid="1"><animals="all" zebras="22" dogs="0" birds="4" /><animals="all" workers="yes" vacation="occasion" /></zooid>'
, #text2 varchar(max) = '<a zooid="1"><b animals="all" zebras="22" dogs="0" birds="4" /><b animals="all" workers="yes" vacation="occasion" /></a>'
, #xml xml
;
begin try
set #xml = cast(#text1 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData Elements are not named" />'
end catch
select #xml
begin try
set #xml = cast(#text2 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData" />'
end catch
select
#xml.value('(/a/b/#animals)[1]', 'varchar(20)') as AnimalsValue
, #xml.value('(/a/b/#zebras)[1]', 'int') as ZebrasValue
, #xml.value('(/a/b/#dogs)[1]', 'int') as DogsValue
, #xml.value('(/a/b/#birds)[1]', 'int') as BirdsValue
, #xml.value('(/a/b/#workers)[1]', 'varchar(16)') as Workers
, #xml.value('(/a/b/#vacation)[1]', 'varchar(16)') as Vacation
The '.value' method is a syntax for querying xml in SQL. I am basically finding the elements(I did generics of a that contained b). Then once at the level I want '#animals' stands for 'attribute of name animals'. The [1] is a position since I can only return one thing at a time, so I chose the first position. Then it needs to a datatype to return. Text is varchar and numbers are ints.
XML query methods: http://msdn.microsoft.com/en-us/library/ms190798.aspx

Resources