Reputation: 69
I am working with a vendor's database that has some XML data as such:
<ArrayOfAlertConditionShelve xmlns="http://schemas.datacontract.org/2004/07/VendorName.Alerting" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AlertConditionShelve>
<AndThenTimeInterval i:nil="true" />
<ChainType>Trigger</ChainType>
<ConditionTypeID>Core.Dynamic</ConditionTypeID>
<Configuration>
<AlertConditionDynamic xmlns="http://schemas.datacontract.org/2004/07/VendorName.Dynamic" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ExprTree xmlns:a="http://schemas.datacontract.org/2004/07/VendorName.Alerting">
<a:Child>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ApplicationAlert|ApplicationName|Application.ApplicationAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>AppNameABC123</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>NodesCustomProperties|n_mute|Application.Node.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>false</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ApplicationCustomProperties|a_mute|Application.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>false</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>NodesCustomProperties|Prod_State|Application.Node.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>PROD</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>Down</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>Critical</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>OR</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>AND</a:Value>
</ExprTree>
<Scope xmlns:a="http://schemas.datacontract.org/2004/07/VendorName.Alerting" i:nil="true" />
<TimeWindow i:nil="true" />
</AlertConditionDynamic>
</Configuration>
<ConjunctionOperator>None</ConjunctionOperator>
<IsInvertedMinCountThreshold>false</IsInvertedMinCountThreshold>
<NetObjectsMinCountThreshold i:nil="true" />
<ObjectType>APM: Component</ObjectType>
<SustainTime>PT5M</SustainTime>
</AlertConditionShelve>
</ArrayOfAlertConditionShelve>
The requirement I have is to return the value for the "a:value" element for each "a:NodeType" element. In addition, I need the string between the pipes { | | } in the "a:NodeType" element, though I can get that with a substring after the fact if it muddles up this solution too much.
I also need to see the values of the "ObjectType" and "SustainTime" elements. To add even more complexity, the "SustainTime" element doesn't always exist.
I've done a fair bit of searching through both StackOverflow and several general google searches digging through various websites and blogs, but unfortunately I just cannot seem to put the disparate pieces together.
For reference, this is the result I am looking for with the example above:
+-----------------------+----------+----------------+
| NodeType | Operator | Value |
+-----------------------+----------+----------------+
| ApplicationName | = | AppNameABC123 |
| n_mute | = | false |
| a_mute | = | false |
| ProdState | = | PROD |
| ComponentAvailability | = | Critical |
| ComponentAvailability | = | Down |
| ObjectType | | APM: Component |
| SustainTime | | PT5M |
+-----------------------+----------+----------------+
Couple of Notes:
This is and example of the associated SQL Query that would match this XML
SELECT * FROM Table
WHERE ( ApplicationName = 'AppNameABC123' AND n_mute = 0 AND a_mute = 0 AND ProdState = 'PROD')
AND ( ComponentAvailability = 'Critical' OR ComponentAvailability = 'Down' )
I appreciate any and all help or guidance! I am pretty solid with SQL Server queries as a rule, but diving into XML has been humbling to say the least.
EDIT
I've made a bit of progress after stumbling upon an article that made a little more sense to my brain:
https://www.red-gate.com/simple-talk/sql/learn-sql-server/the-xml-methods-in-sql-server/
-- exist() Method
-- DbObject.exist('XQuery')
-- 1 if the XQuery expression returns a nonempty result
-- 0 if the XQuery expression returns an empty result.
-- NULL value if the XML data type instance is null.
SELECT
v.AlertID
,v.alertTriggerQuery.value(
'(/*:Expr/*:NodeType) [1]','varchar(max)'
) 'alertTriggerQueryNodeTypeValue'
,v.alertTriggerQuery.value(
'(/*:Expr/*:Value) [1]','varchar(max)'
) 'alertTriggerQueryValue'
,v.objectType.value(
'(/*:ObjectType) [1]', 'varchar(100)'
) 'alertTriggerValue'
,v.sustainTime.value(
'(/*:SustainTime) [1]', 'varchar(10)'
) 'sustainTimeValue'
,v.sustainTime.value('concat("TRIGGER DELAY: ",
(/*:SustainTime) [1])', 'varchar(100)'
) 'sustainTimeValueConcat'
FROM
(SELECT
q.AlertID
,q.triggerXML.query('
/*:ArrayOfAlertConditionShelve/*:AlertConditionShelve/*:Configuration/*:AlertConditionDynamic/*:ExprTree/*:Child/*:Expr/*:Child/*:Expr
') 'alertTriggerQuery'
,q.triggerXML.query('
/*:ArrayOfAlertConditionShelve/*:AlertConditionShelve/*:ObjectType
') 'objectType'
,q.triggerXML.query('
/*:ArrayOfAlertConditionShelve/*:AlertConditionShelve/*:SustainTime
') 'sustainTime'
FROM (
SELECT
AlertID
,CAST(REPLACE(REPLACE([Trigger],'<','<'),'>','>') AS XML) 'triggerXML'
FROM AlertConfigurations
WHERE AlertID IN ( 280, 3052 )
) q
WHERE q.triggerXML.exist('/*:ArrayOfAlertConditionShelve/*:AlertConditionShelve[*:SustainTime="PT5M"]') = 1
) v;
GO
Using a combination of the query(), value(), and exist() methods, I have been able to drill into the XML and get the following data out:
+---------+--------------------------------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
| AlertID | alertTriggerQueryNodeTypeValue | alertTriggerQueryValue | alertTriggerValue | sustainTimeValue | sustainTimeValueConcat |
+---------+--------------------------------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
| 280 | Field | APM.ApplicationAlert|ApplicationName|Application.ApplicationAlert | APM:Component | PT5M | TRIGGER DELAY: PT5M |
+---------+--------------------------------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
So, I think the next step is to figure out how to get the detailed information out of the 'alertTriggerQuery' XML in my subselect statement; which looks like this:
<p1:Expr xmlns:p1="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p1:Child />
<p1:NodeType>Field</p1:NodeType>
<p1:Value>APM.ApplicationAlert|ApplicationName|Application.ApplicationAlert</p1:Value>
</p1:Expr>
<p2:Expr xmlns:p2="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p2:Child />
<p2:NodeType>Constant</p2:NodeType>
<p2:Value>WINWATCHER_WEB_02_URL</p2:Value>
</p2:Expr>
<p3:Expr xmlns:p3="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p3:Child />
<p3:NodeType>Field</p3:NodeType>
<p3:Value>NodesCustomProperties|n_mute|Application.Node.CustomProperties</p3:Value>
</p3:Expr>
<p4:Expr xmlns:p4="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p4:Child />
<p4:NodeType>Constant</p4:NodeType>
<p4:Value>false</p4:Value>
</p4:Expr>
<p5:Expr xmlns:p5="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p5:Child />
<p5:NodeType>Field</p5:NodeType>
<p5:Value>APM.ApplicationCustomProperties|a_mute|Application.CustomProperties</p5:Value>
</p5:Expr>
<p6:Expr xmlns:p6="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p6:Child />
<p6:NodeType>Constant</p6:NodeType>
<p6:Value>false</p6:Value>
</p6:Expr>
<p7:Expr xmlns:p7="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p7:Child />
<p7:NodeType>Field</p7:NodeType>
<p7:Value>NodesCustomProperties|Prod_State|Application.Node.CustomProperties</p7:Value>
</p7:Expr>
<p8:Expr xmlns:p8="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p8:Child />
<p8:NodeType>Constant</p8:NodeType>
<p8:Value>PROD</p8:Value>
</p8:Expr>
<p9:Expr xmlns:p9="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<p9:Child>
<p9:Expr>
<p9:Child />
<p9:NodeType>Field</p9:NodeType>
<p9:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</p9:Value>
</p9:Expr>
<p9:Expr>
<p9:Child />
<p9:NodeType>Constant</p9:NodeType>
<p9:Value>Down</p9:Value>
</p9:Expr>
</p9:Child>
<p9:NodeType>Operator</p9:NodeType>
<p9:Value>=</p9:Value>
</p9:Expr>
<pA:Expr xmlns:pA="http://schemas.datacontract.org/2004/07/VENDORNAME.Alerting">
<pA:Child>
<pA:Expr>
<pA:Child />
<pA:NodeType>Field</pA:NodeType>
<pA:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</pA:Value>
</pA:Expr>
<pA:Expr>
<pA:Child />
<pA:NodeType>Constant</pA:NodeType>
<pA:Value>Critical</pA:Value>
</pA:Expr>
</pA:Child>
<pA:NodeType>Operator</pA:NodeType>
<pA:Value>=</pA:Value>
</pA:Expr>
The current hurdle is figuring out how to return a single result table that iterates every parent node's values for "NodeType" and "Value"
i.e.:
+---------+------+----------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
| AlertID | Node | NodeType | Value | alertTriggerValue | sustainTimeValue | sustainTimeValueConcat |
+---------+------+----------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
| 280 | p1 | Field | APM.ApplicationAlert|ApplicationName|Application.ApplicationAlert | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | p2 | Constant | WINWATCHER_WEB_02_URL | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | ... | ... | ... | ... | ... | ... |
| 280 | p9 | Field | APM.ComponentAlert|ComponentAvailability|ComponentAlert | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | p9 | Constant | Down | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | p9 | Operator | = | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | pA | Field | APM.ComponentAlert|ComponentAvailability|ComponentAlert | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | pA | Constant | Critical | APM:Component | PT5M | TRIGGER DELAY: PT5M |
| 280 | pA | Operator | = | APM:Component | PT5M | TRIGGER DELAY: PT5M |
+---------+------+----------+-------------------------------------------------------------------+-------------------+------------------+------------------------+
I believe my current limitation is in my "v.alertTriggerQuery.value()" statement where I have to call out the node position "[1]". In PowerShell, I would look at doing something similar to a Foreach loop after placing all of the nodes into an array; but I am not sure how to translate that logic into SQL.
Upvotes: 3
Views: 1752
Reputation: 67321
There is no easy going I'm afraid...
DECLARE @yourTable TABLE(ID INT IDENTITY, TheXml XML);
INSERT INTO @yourTable VALUES
(N'<ArrayOfAlertConditionShelve xmlns="http://schemas.datacontract.org/2004/07/VendorName.Alerting" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AlertConditionShelve>
<AndThenTimeInterval i:nil="true" />
<ChainType>Trigger</ChainType>
<ConditionTypeID>Core.Dynamic</ConditionTypeID>
<Configuration>
<AlertConditionDynamic xmlns="http://schemas.datacontract.org/2004/07/VendorName.Dynamic" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ExprTree xmlns:a="http://schemas.datacontract.org/2004/07/VendorName.Alerting">
<a:Child>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ApplicationAlert|ApplicationName|Application.ApplicationAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>AppNameABC123</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>NodesCustomProperties|n_mute|Application.Node.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>false</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ApplicationCustomProperties|a_mute|Application.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>false</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>NodesCustomProperties|Prod_State|Application.Node.CustomProperties</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>PROD</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>Down</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
<a:Expr>
<a:Child>
<a:Expr>
<a:Child />
<a:NodeType>Field</a:NodeType>
<a:Value>APM.ComponentAlert|ComponentAvailability|ComponentAlert</a:Value>
</a:Expr>
<a:Expr>
<a:Child />
<a:NodeType>Constant</a:NodeType>
<a:Value>Critical</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>=</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>OR</a:Value>
</a:Expr>
</a:Child>
<a:NodeType>Operator</a:NodeType>
<a:Value>AND</a:Value>
</ExprTree>
<Scope xmlns:a="http://schemas.datacontract.org/2004/07/VendorName.Alerting" i:nil="true" />
<TimeWindow i:nil="true" />
</AlertConditionDynamic>
</Configuration>
<ConjunctionOperator>None</ConjunctionOperator>
<IsInvertedMinCountThreshold>false</IsInvertedMinCountThreshold>
<NetObjectsMinCountThreshold i:nil="true" />
<ObjectType>APM: Component</ObjectType>
<SustainTime>PT5M</SustainTime>
</AlertConditionShelve>
</ArrayOfAlertConditionShelve>');
--Some general hints:
--Easy-Cheesy: The two meta-data values
SELECT t.TheXml.value(N'(//*:ObjectType/text())[1]',N'nvarchar(max)') AS ObjectType
,t.TheXml.value(N'(//*:SustainTime/text())[1]',N'nvarchar(max)') AS SustainTime
FROM @yourTable AS t;
--Specified default namespace, full XPath
WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/VendorName.Alerting')
SELECT t.TheXml.value(N'(/ArrayOfAlertConditionShelve/AlertConditionShelve/ObjectType/text())[1]',N'nvarchar(max)') AS ObjectType
,t.TheXml.value(N'(/ArrayOfAlertConditionShelve/AlertConditionShelve/SustainTime/text())[1]',N'nvarchar(max)') AS SustainTime
FROM @yourTable AS t
--You can use the "deep search" with //
to find all elements, but this won't really help:
--Eays-Cheesy: All NodeTypes with their Values but not in the correct order and nesting
SELECT AnyExprWithNodeType.value(N'(*:NodeType/text())[1]',N'nvarchar(max)') AS NodeType
,AnyExprWithNodeType.value(N'(*:Value/text())[1]',N'nvarchar(max)') AS NodeValue
FROM @yourTable AS t
CROSS APPLY t.TheXml.nodes(N'//*:Expr[*:NodeType]') AS A(AnyExprWithNodeType);
--Try to solve this (no deeper nesting) --This works until "OR", the rest is deeper nested.
WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/VendorName.Alerting'
,'http://schemas.datacontract.org/2004/07/VendorName.Alerting' AS a
,'http://schemas.datacontract.org/2004/07/VendorName.Dynamic' AS innerDflt)
SELECT FirstLvlExpr.value(N'(a:Child//a:Expr[a:NodeType/text()="Field"]/a:Value/text())[1]','nvarchar(max)') AS NodeType
,FirstLvlExpr.value(N'(a:Value/text())[1]','nvarchar(max)') AS Operator
,FirstLvlExpr.value(N'(a:Child//a:Expr[a:NodeType/text()="Constant"]/a:Value/text())[1]','nvarchar(max)') AS Value
FROM @yourTable AS t
CROSS APPLY t.TheXml.nodes(N'/ArrayOfAlertConditionShelve
/AlertConditionShelve
/Configuration
/innerDflt:AlertConditionDynamic
/innerDflt:ExprTree
/a:Child
/a:Expr') AS A(FirstLvlExpr);
I assume, that such a tree can be very complex and hierarchically nested. You might use a recursive CTE or some kind of repeated CROSS APPLY
with .nodes()
to dive deeper and deeper.
Hope this helps...
The following statement will start with all elements <NodeType>
where the text()
is Field. The call to .nodes()
creates a derived table from all fitting nodes, wherever they are.
The next step uses backward-navigation (..
) to climb up the parents and find the related value and operator. Try it out:
WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/VendorName.Alerting'
,'http://schemas.datacontract.org/2004/07/VendorName.Alerting' AS a
,'http://schemas.datacontract.org/2004/07/VendorName.Dynamic' AS innerDflt)
SELECT B.NodeTypeSplitted
,B.NodeTypeSplitted.value(N'/*:x[2]/text()[1]',N'nvarchar(max)') AS SecondPartOfNodeType
,Fields.value(N'(../../../a:Value/text())[1]','nvarchar(max)') AS Operator
,Fields.value(N'(../../a:Expr[a:NodeType[text()="Constant"]]/a:Value/text())[1]','nvarchar(max)') AS NodeType
FROM @yourTable AS t
CROSS APPLY t.TheXml.nodes(N'//a:NodeType[text()="Field"]') AS A(Fields)
CROSS APPLY (SELECT CAST('<x>' + REPLACE((SELECT Fields.value(N'(../a:Value/text())[1]','nvarchar(max)') AS [*] FOR XML PATH('')),'|','</x><x>') + '</x>' AS XML) AS NodeTypeSplitted) AS B;
The result
SecondPartOfNodeType Operator NodeType
ApplicationName = AppNameABC123
n_mute = false
a_mute = false
Prod_State = PROD
ComponentAvailability = Down
ComponentAvailability = Critical
The splitting of the piped values is done with an XML trick.
Upvotes: 4