I have a database field containing XML nested like this:
<configuration>
<modules>
<genericMailer>
<moduleContent>
<data>
<sendMethod>Email</sendMethod>
My XML can contain many instances of the <genericMailer> element (and it's subnodes). I want to find all rows where there are any instances of <genericMailer> where the <sendMethod> contains 'Mail'.
I can get all of the values for sendMethod like this:
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol);
however I don't know how to search only for matching values. What's the WHERE clause I need?
Here are some possible ways :
a. Using value() method to extract sendMethod value then check if the value LIKE '%mail%' in WHERE clause :
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol)
WHERE x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') LIKE '%mail%'
b. Using XPath method contains() to check if sendMethod value contains substring "mail" and SQL Server method exist() to filter rows that pass the check :
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol)
WHERE x.XmlCol.exist('moduleContent/data/sendMethod[contains(.,"mail")]') = 1
Related
I'm trying to construct a soap message, and I was able to construct the entire message using a single select. Except the problem is, on only a few occasions the same node name is repeated twice.
So for example the required output result should be like so, with two separate id root nodes:
<SoapDocument>
<recordTarget>
<patientRole>
<id root="1.2.3.4" extension="1234567" />
<id root="1.2.3.5.6" extension="0123456789" />
</patientRole>
</recordTarget>
</SoapDocument>
I tried to use my sparse knowledge of xpath to construct the node names like so:
select
'1.2.3.4' AS 'recordTarget/patientRole/id[1]/#root',
'1234567' AS 'recordTarget/patientRole/id[1]/#extension',
'1.2.3.5.6' AS 'recordTarget/patientRole/id[2]/#root',
'0123456789' AS 'recordTarget/patientRole/id[2]/#extension'
FOR XML PATH('SoapDocument'),TYPE
Apparently xpath naming can't be applied to column names id[1] and id[2] like that? Am I missing something here or should the notation be different? What would be the easiest way to constuct the desired result?
From your question I assume, this is not tabular data, but fixed values and you are creating a medical document, assumably a CDA.
Try this:
SELECT
(
SELECT
'1.2.3.4' AS 'id/#root',
'1234567' AS 'id/#extension',
'',
'1.2.3.5.6' AS 'id/#root',
'0123456789' AS 'id/#extension'
FOR XML PATH('patientRole'),TYPE
) AS [SoapDocument/recordTarget]
FOR XML PATH('')
The result:
<SoapDocument>
<recordTarget>
<patientRole>
<id root="1.2.3.4" extension="1234567" />
<id root="1.2.3.5.6" extension="0123456789" />
</patientRole>
</recordTarget>
</SoapDocument>
Some explanation: The empty element in the middle allows you to place two elements with the same name in one query. There are various approaches how you get this into your surrounding tags. This is just one possibility.
UPDATE
I'd like to point to BdR's own answer! Great finding and worth an up-vote!
A little more elaboration on the answer from Shnugo, as it got me trying out some things using an "empty column".
If you do not give the emtpy column a name, it will reset to the XML root node. So the following columns will start from the XML root of the selection you are in at that point. However, if you explicitly name the empty separator column, then the following columns will continue in the hierarchy as set by that column name.
So the selection below will also result in the desired result. It's subtly different, but in my case it allows me to avoid using subselections.
select
'1.2.3.4' AS 'recordTarget/patientRole/id/#root',
'1234567' AS 'recordTarget/patientRole/id/#extension',
'' AS 'recordTarget/patientRole',
'1.2.3.5.6' AS 'recordTarget/patientRole/id/#root',
'0123456789' AS 'recordTarget/patientRole/id/#extension'
FOR XML PATH('SoapDocument'),TYPE
This should do the job:
WITH CTE AS (
SELECT *
FROM (VALUES('1.2.3.4','1234567'),
('1.2.3.5.6','0123456789')) V ([root], [extension]))
SELECT (SELECT (SELECT (SELECT [root] AS [#root],
[extension] AS [#extension]
FROM CTE
FOR XML PATH('id'), TYPE)
FOR XML PATH('patientRole'), TYPE)
FOR XML PATH ('recordTarget'), TYPE)
FOR XML PATH ('SoapDocument');
I have an XML document that I'm working to build a schema for in order to bulk load these documents into a SQL Server table. The XML I'm focusing on looks like this:
<Coverage>
<CoverageCd>BI</CoverageCd>
<CoverageDesc>BI</CoverageDesc>
<Limit>
<FormatCurrencyAmt>
<Amt>30000.00</Amt>
</FormatCurrencyAmt>
<LimitAppliesToCd>PerPerson</LimitAppliesToCd>
</Limit>
<Limit>
<FormatCurrencyAmt>
<Amt>85000.00</Amt>
</FormatCurrencyAmt>
<LimitAppliesToCd>PerAcc</LimitAppliesToCd>
</Limit>
</Coverage>
<Coverage>
<CoverageCd>PD</CoverageCd>
<CoverageDesc>PD</CoverageDesc>
<Limit>
<FormatCurrencyAmt>
<Amt>50000.00</Amt>
</FormatCurrencyAmt>
<LimitAppliesToCd>Coverage</LimitAppliesToCd>
</Limit>
</Coverage>
Inside the Limit element, there's a child LimitAppliesToCd that I need to use to determine where the Amt element's value actually gets stored inside my table. Is this possible to do using the standard XML Bulk Load feature of SQL Server? Normally in XML I'd expect that the element would have an attribute containing the "PerPerson" or "PerAcc" information, but this standard we're using does not call for that.
If anyone has worked with the ACORD standard before, you might know what I'm working with here. Any help is greatly appreciated.
Don't know exactly what you are talking about, but this is a solution to get the information out of your XML.
Assumption: Your XML is already bulk-loaded into a declared variable #xml of type XML:
A CTE will pull the information out of your XML. The final query will then use PIVOT to put your data into the right column.
With a fitting table's structure the actual insert should be simple...
WITH DerivedTable AS
(
SELECT cov.value('CoverageCd[1]','varchar(max)') AS CoverageCd
,cov.value('CoverageDesc[1]','varchar(max)') AS CoverageDesc
,lim.value('(FormatCurrencyAmt/Amt)[1]','decimal(14,4)') AS Amt
,lim.value('LimitAppliesToCd[1]','varchar(max)') AS LimitAppliesToCd
FROM #xml.nodes('/root/Coverage') AS A(cov)
CROSS APPLY cov.nodes('Limit') AS B(lim)
)
SELECT p.*
FROM
(SELECT * FROM DerivedTable) AS tbl
PIVOT
(
MIN(Amt) FOR LimitAppliesToCD IN(PerPerson,PerAcc,Coverage)
) AS p
I have this query taken from the site www.SQLauthority.com:
DECLARE #MyXML XML
SET #MyXML = '<SampleXML>
<Colors>
<Color1>White</Color1>
<Color2>Blue</Color2>
<Color3>Black</Color3>
<Color4 Special="Light">Green</Color4>
<Color5>Red</Color5>
</Colors>
<Fruits>
<Fruits1>Apple</Fruits1>
<Fruits2>Pineapple</Fruits2>
<Fruits3>Grapes</Fruits3>
<Fruits4>Melon</Fruits4>
</Fruits>
</SampleXML>'
SELECT
a.b.value('Colors[1]/Color1[1]','varchar(10)') AS Color1,
a.b.value('Colors[1]/Color2[1]','varchar(10)') AS Color2,
a.b.value('Colors[1]/Color3[1]','varchar(10)') AS Color3,
a.b.value('Colors[1]/Color4[1]/#Special','varchar(10)')+' '+
+a.b.value('Colors[1]/Color4[1]','varchar(10)') AS Color4,
a.b.value('Colors[1]/Color5[1]','varchar(10)') AS Color5,
a.b.value('Fruits[1]/Fruits1[1]','varchar(10)') AS Fruits1,
a.b.value('Fruits[1]/Fruits2[1]','varchar(10)') AS Fruits2,
a.b.value('Fruits[1]/Fruits3[1]','varchar(10)') AS Fruits3,
a.b.value('Fruits[1]/Fruits4[1]','varchar(10)') AS Fruits4
FROM #MyXML.nodes('SampleXML') a(b)
I am not getting a better picture of how the nodes fetching from the xml data.
I have few queries regarding this.
what is a(b) in this?
how the structure will change if i have another node inside colors and all the existing child nodes appended to that?
ie:
<Colorss>
<Colors>
<Color1>White</Color1>
<Color2>Blue</Color2>
<Color3>Black</Color3>
<Color4 Special="Light">Green</Color4>
<Color5>Red</Color5>
</Colors>
<Colorss>
<Fruits>
<Fruits1>Apple</Fruits1>
<Fruits2>Pineapple</Fruits2>
<Fruits3>Grapes</Fruits3>
<Fruits4>Melon</Fruits4>
</Fruits>
what does it mean by a.b.value? When I mouse over it shows a is derived table. Can I check value of the table a?
Any help in this will be appreciated.
what is a(b) in this?
The call to .nodes('SampleXML') is a XQuery function which returns a pseudo table which contains one column of an XML fragment for each of the elements that this XPath expression matches - and the a(b) is the table alias (a) for that column, and b is the name of the column in that pseudo table containing the XML fragments.
what does it mean by a.b.value?
This is based on the above - a is the table alias for that temporary, inline pseudo table, b is the column name for the column in that table, and .value() is another XQuery function that will extract a single value from XML, based on the XPath expression (first argument) and it will return it as the datatype specified in the second argument.
You should check out those introductions to XQuery support in SQL Server to understand better:
Introduction to XQuery in SQL Server 2005
XQuery basics
and there are numerous other introductions and tutorials on XQuery - just search with your favorite search engine and you'll get tons of hits!
here's my stab # it:
a-refers to root;b-refers to root and child node
DECLARE #MyXML XML
SET #MyXML = '<SampleXML>
<Colors>
<Color1>White</Color1>
<Color2>Blue</Color2>
<Color3>Black</Color3>
<Color4 Special="Light">Green</Color4>
<Color5>Red
<Color6>Black44</Color6>
<Color7>Black445</Color7>
</Color5>
</Colors>
<Fruits>
<Fruits1>Apple</Fruits1>
<Fruits2>Pineapple</Fruits2>
<Fruits3>Grapes</Fruits3>
<Fruits4>Melon</Fruits4>
</Fruits>
</SampleXML>'
to get an inner child
SELECT
a.c.value('Colors1/Color11','varchar(10)') AS Color1,
a.c.value('Colors1/Color21','varchar(10)') AS Color2,
a.c.value('Colors1/Color31','varchar(10)') AS Color3,
a.c.value('Colors1/Color41/#Special','varchar(10)') AS Color4,
a.c.value('Colors1/Color51','varchar(10)') AS Color5,
a.c.value('Colors1/Color51/Color71','varchar(50)') AS Color6a,
a.c.value('Colors1/Color51/Color61','varchar(50)') AS Color6b, a.c.value('Fruits1/Fruits11','varchar(10)') AS Fruits1,
a.c.value('Fruits1/Fruits21','varchar(10)') AS Fruits2,
a.c.value('Fruits1/Fruits31','varchar(10)') AS Fruits3,
a.c.value('Fruits1/Fruits41','varchar(10)') AS Fruits4
FROM #MyXML.nodes('SampleXML') a(c)
A nodes() method invocation with the query expression /root/Color(n) would return a rowset with three rows, each containing a logical copy of the original XML document, and with the context item set to one of the nodes
see here
Hi I need to populate table by selecting value from child node
XML looks like this
<Transmitters>
<Id>1</Id><CoverageLevel>2</CoverageLevel>
<Id>2</Id><CoverageLevel>4</CoverageLevel>
<Id>3</Id><CoverageLevel>6</CoverageLevel>
</Transmitters>
and table has two fields
Transmitter(Id, CoverageLevel)
INSERT INTO
Transmitter([idTransmitter], [coverageLevel])
SELECT
ParamValues.T.value('Id[1]', 'nvarchar(50)'),
ParamValues.T.value('CoverageLevel[1]', 'nvarchar(50)')
FROM
#otherTransmitter.nodes('//Transmitters') AS ParamValues(T)
but its not working?
Your XML is not very well formatted for this kind of task - you don't have any useable child nodes inside <Transmitters> that you can depend on.
If your XML looked like this:
<Transmitters>
<Transmitter>
<Id>1</Id><CoverageLevel>2</CoverageLevel>
</Transmitter>
<Transmitter>
<Id>2</Id><CoverageLevel>4</CoverageLevel>
</Transmitter>
<Transmitter>
<Id>3</Id><CoverageLevel>6</CoverageLevel>
</Transmitter>
</Transmitters>
then you could use the XPath expression
#otherTransmitter.nodes('/Transmitters/Transmitter') AS ParamValues(T)
to get a hold of your values.
You don't have that - you only have individual <Id> and <CoverageLevel> inside your <Transmitters> - there's no "container" XML tag that holds together those elements that belong together.
There's really no XPath that will allow you to properly enumerate those nodes.....
I have something like the following XML in a column of a table:
<?xml version="1.0" encoding="utf-8"?>
<container>
<param name="paramA" value="valueA" />
<param name="paramB" value="valueB" />
...
</container>
I am trying to get the valueB part out of the XML via TSQL
So far I am getting the right node, but now I can not figure out how to get the attribute.
select xmlCol.query('/container/param[#name="paramB"]') from LogTable
I figure I could just add /#value to the end, but then SQL tells me attributes have to be part of a node. I can find a lot of examples for selecting the child nodes attributes, but nothing on the sibling atributes (if that is the right term).
Any help would be appreciated.
Try using the .value function instead of .query:
SELECT
xmlCol.value('(/container/param[#name="paramB"]/#value)[1]', 'varchar(50)')
FROM
LogTable
The XPath expression could potentially return a list of nodes, therefore you need to add a [1] to that potential list to tell SQL Server to use the first of those entries (and yes - that list is 1-based - not 0-based). As second parameter, you need to specify what type the value should be converted to - just guessing here.
Marc
Depending on the the actual structure of your xml, it may be useful to put a view over it to make it easier to consume using 'regular' sql eg
CREATE VIEW vwLogTable
AS
SELECT
c.p.value('#name', 'varchar(10)') name,
c.p.value('#value', 'varchar(10)') value
FROM
LogTable
CROSS APPLY x.nodes('/container/param') c(p)
GO
-- now you can get all values for paramB as...
SELECT value FROM vwLogTable WHERE name = 'paramB'