May 26, 2017 at 12:28 am
I'm working on an expansion of an incredible idea by Fabiano Amorimthat (https://blogfabiano.com/2012/07/03/statistics-used-in-a-cached-query-plan/) which uses Trace Flag 8666 to add some nifty stuff about which statistics a query plan used. The trouble is that my XML skills couldn't be labeled as "skills". I'm terrible at shredding it the way I want and could really use some help, please.
I've attached a text file (StatsXML.txt) that contains a CREATE TABLE statement (in TempDB to be safe) and populates it with 2 example XML Query Plans. If you run that code and then run a slight modification of Fabiano's code (below), you get the result set that follows the code.
DBCC TRACEON(8666)
GO
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,StatsName = StatsUsed.XMLCol.value('@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:Field[@FieldName="wszStatName"]') StatsUsed (XMLCol)
;
GO
DBCC TRACEOFF(8666)
GO
Results:
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
RowNum StatsName
----------- --------------------------------------------------------------------------------
1 AK_AreaCodeTimezone
1 IX_BY_TimezoneNumber_ObservesDaylightSaving_ActivityStatusCode
1 _WA_Sys_0000000B_36F11965
1 _WA_Sys_00000005_36F11965
1 AK_CRM_TimezoneEnum
1 _WA_Sys_00000003_2E5BD364
1 _WA_Sys_0000000C_2E5BD364
1 _WA_Sys_00000005_2E5BD364
1 _WA_Sys_0000000B_2E5BD364
1 AK_CRM_TimezoneEnum
1 _WA_Sys_00000003_2E5BD364
1 _WA_Sys_0000000B_2E5BD364
1 _WA_Sys_0000000C_2E5BD364
1 _WA_Sys_00000005_2E5BD364
2 PK_Tally_N
(15 row(s) affected)
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
The problem is that this is for the whole server and so having just the stats name won't help so much. What we need is the database name, schema name, and table name that the stats name belongs to. Like this...
"Flattened"/denormalized output I'm looking for. Note that the duplication exists in the XML and I can deal with that later (I think 😉 ).
|<FrmTbl>| |<-- From multiple "Recompile" tags -->| |<-------- From multiple "ModTrackingInfo" tags within each "Recompile" tag -------->|
RowNum wszDb wszSchema wszTable wszColName wszStatName
------ -------- -------- ---------------- ---------------------- --------------------------------------------------------------
1 CRM_Prod Util AreaCodeTimezone AreaCodeExchange AK_AreaCodeTimezone
1 CRM_Prod Util AreaCodeTimezone TimezoneNumber IX_BY_TimezoneNumber_ObservesDaylightSaving_ActivityStatusCode
1 CRM_Prod Util AreaCodeTimezone ActivityStatusCode _WA_Sys_0000000B_36F11965
1 CRM_Prod Util AreaCodeTimezone ObservesDaylightSaving _WA_Sys_00000005_36F11965
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneCode AK_CRM_TimezoneEnum
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneNumber _WA_Sys_00000003_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ActivityStatusCode _WA_Sys_0000000C_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum IsDaylightSaving _WA_Sys_00000005_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ServerInDaylightSaving _WA_Sys_0000000B_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneCode AK_CRM_TimezoneEnum
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneNumber _WA_Sys_00000003_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ServerInDaylightSaving _WA_Sys_0000000B_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ActivityStatusCode _WA_Sys_0000000C_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum IsDaylightSaving _WA_Sys_00000005_2E5BD364
2 JBMTest dbo Tally N PK_Tally_N
The really cool part about Fabiano's finding is that all of that information is included in the XML. The really bad part is, I can't figure out how to get it in the form above.
A bit about the XML... at some point in the hierarchy of the XML, there are one or more "Recompile" tags. Each of those tags contain entities that look like this...
<Recompile>
<Field FieldName="wszDb" FieldValue="CRM_Prod" />
<Field FieldName="wszSchema" FieldValue="Util" />
<Field FieldName="wszTable" FieldValue="AreaCodeTimezone" />
<Field FieldName="m_cRowCount" FieldValue="237767" />
<Field FieldName="ullThreshold" FieldValue="48053" />
(continues with multiple "ModTrackingInfo" elements)
What I'm after are the "FieldValue"s for the "FieldName"s of wszDb, wszSchema, and wszTable.
Also included within the Recompile element are other elements (one or more ModTrackingInfo elements) that look like this...
<ModTrackingInfo>
<Field FieldName="wszStatName" FieldValue="AK_AreaCodeTimezone" />
<Field FieldName="wszColName" FieldValue="AreaCodeExchange" />
<Field FieldName="m_cCols" FieldValue="1" />
<Field FieldName="m_idIS" FieldValue="1" />
<Field FieldName="m_ullSnapShotModCtr" FieldValue="237767" />
<Field FieldName="m_ullRowCount" FieldValue="237767" />
<Field FieldName="ullThreshold" FieldValue="48053" />
<Field FieldName="wszReason" FieldValue="heuristic" />
</ModTrackingInfo>
... and I'd like get not only the "FieldValue" for the wszStatName "FieldName" but also the "FieldValue" for the wszColName and wszStatName "FieldName"s. Basically, I want to "flatten" out the values into a denormalized table like I posted above. Everything I've tried resulted in a rather nasty Cartesian product for XML like RowNum = 1 because of the multiple Recompile elements each having multiple ModTrackingInfo elements and all of those each having multiple entities.
Help? Please? Thanks, folks.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2017 at 5:18 am
Hi Jeff,
This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537May 26, 2017 at 5:27 am
Already great work by Mark 🙂
I only came to this starting point:
Select PlanXML.xmlnode.value('@FieldName', 'varchar(255)') AS FieldName
, PlanXML.xmlnode.value('@FieldValue', 'varchar(255)') AS FieldValue
from #XMLTest QP
OUTER APPLY QP.TheXML.nodes('//ModTrackingInfo/Field') PlanXML(xmlnode)
WHERE QP.TheXML.exist(N'//ModTrackingInfo') =1
Most of my XMLcoding is copy / paste based. If the results are what I expect them to be, never touch again 🙂 ... euhm 🙁
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution 😀
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
May 26, 2017 at 7:35 am
Mark Cowne - Friday, May 26, 2017 5:18 AMHi Jeff,This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);
OMG! Something's wrong! I actually understand what you did and it's XML! 😉 What a great example!
And I just ran your code... TOTALLY AWESOME! Thank you so much for the help. I actually understand what you did and I'm completely embarrassed at how simple you made it. It definitely shows what can be done if you know what you're doing.
Shifting gears to that very subject, I do understand everything that you're done and I recognize all of the pieces that you used to do it. I've struggled many times over many days trying to figure this out on my own. I've watched videos and have read a lot of posts and articles and still couldn't put this (apparently) simple bit of XML shredding together. I do realize that it takes practice but what on this good Green Earth did you use to train yourself in all things XML?.
I can't thank you enough for the help you've provided. You've absolutely made my day. Thanks, Mark.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2017 at 7:42 am
ALZDBA - Friday, May 26, 2017 5:27 AMAlready great work by Mark 🙂I only came to this starting point:
Select PlanXML.xmlnode.value('@FieldName', 'varchar(255)') AS FieldName
, PlanXML.xmlnode.value('@FieldValue', 'varchar(255)') AS FieldValue
from #XMLTest QP
OUTER APPLY QP.TheXML.nodes('//ModTrackingInfo/Field') PlanXML(xmlnode)
WHERE QP.TheXML.exist(N'//ModTrackingInfo') =1Most of my XMLcoding is copy / paste based. If the results are what I expect them to be, never touch again 🙂 ... euhm 🙁
Thanks for stepping up to the plate! I've had to rely on the "CPR" method ("Copy, Paste, Replace") method for XML coding myself to keep things alive at times. You've also provided a valuable extra tip with that WHERE clause. Thank you Johan!
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2017 at 7:43 am
Jeff Moden - Friday, May 26, 2017 7:35 AMMark Cowne - Friday, May 26, 2017 5:18 AMHi Jeff,This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);OMG! Something's wrong! I actually understand what you did and it's XML! 😉 What a great example!
And I just ran your code... TOTALLY AWESOME! Thank you so much for the help. I actually understand what you did and I'm completely embarrassed at how simple you made it. It definitely shows what can be done if you know what you're doing.
Shifting gears to that very subject, I do understand everything that you're done and I recognize all of the pieces that you used to do it. I've struggled many times over many days trying to figure this out on my own. I've watched videos and have read a lot of posts and articles and still couldn't put this (apparently) simple bit of XML shredding together. I do realize that it takes practice but what on this good Green Earth did you use to train yourself in all things XML?.
I can't thank you enough for the help you've provided. You've absolutely made my day. Thanks, Mark.
Hi Jeff,
You're very welcome. As for how to do XML stuff - for me it's just practise. There are a few gurus that regularly post XML here (you know who you are!), well worth studying their solutions, you can learn a load.
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537May 26, 2017 at 10:49 am
Studying Mark's good solution for this, the code has an interesting parallel in T-SQL known as the "CROSSTAB".
Just a bit of a followup to help those that may be struggling with XML like I am...
Looking at the following line from Mark's script...
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
... I can "see" the following T-SQL Equivalent... (the [1] in the XML mean "first instance")
,StatsName = MIN(CASE WHEN FieldName = 'wzxStatName' THEN CAST(FieldValue AS NVARCHAR(128) ELSE '' END)
In the CROSS APPLY, we find...
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);
That's kind of like a GROUP BY. The "//p:" thingy is a "wild card" lookup that says "starting at the top of the XML, go find any and all instances of the "ModTrackingInfo" element and "group" by each one found. The dot-dot things in the SELECT list mean pretty much the same as they do in DOS directory commands. It means "Wherever I'm at, look in the parent folder (or element in the case of XML, which has a lot of tree parallels)". "Where we're at" is defined in the CROSS APPLY.
I'll learn this stuff yet. Thanks again, Mark.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2017 at 11:12 am
BWAAAA-HAAAA!!!! Too funny! I just saw Mark's tagline in his signature line.
Deja View - The strange feeling that somewhere, sometime you've optimised this query before.
I'm thinking that would make one hell of a cool T-Shirt, Mark. Ok if I borrow that?
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2017 at 11:29 am
i
Jeff Moden - Friday, May 26, 2017 11:12 AMBWAAAA-HAAAA!!!! Too funny! I just saw Mark's tagline in his signature line.
Deja View - The strange feeling that somewhere, sometime you've optimised this query before.
I'm thinking that would make one hell of a cool T-Shirt, Mark. Ok if I borrow that?
Yep, no probs. I suspect I borrowed it from someone else.
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537May 27, 2017 at 4:11 am
Here is an alternative which extracts all the FieldName/FieldValue attributes from the execution plan as an EAV type output, quite handy for quick analysis or as a source for Pivot/PowerBI etc.
😎
-- Using namespaces
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,STU.DATA.value('local-name(../../.)','NVARCHAR(128)') AS PARENT
,STU.DATA.value('local-name(../.)' ,'NVARCHAR(128)') AS ELEMENT
,STU.DATA.value('@FieldName' ,'NVARCHAR(128)') AS FieldName
,STU.DATA.value('@FieldValue' ,'NVARCHAR(128)') AS FieldVAlue
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:Recompile') RECOMP(DATA)
CROSS APPLY RECOMP.DATA.nodes('//p:Field') STU(DATA);
-- Ignoring namespaces
SELECT xt.RowNum
,STU.DATA.value('local-name(../../.)','NVARCHAR(128)') AS PARENT
,STU.DATA.value('local-name(../.)' ,'NVARCHAR(128)') AS ELEMENT
,STU.DATA.value('@FieldName' ,'NVARCHAR(128)') AS FieldName
,STU.DATA.value('@FieldValue' ,'NVARCHAR(128)') AS FieldVAlue
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//*:Recompile') RECOMP(DATA)
CROSS APPLY RECOMP.DATA.nodes('//*:Field') STU(DATA);
Sample outputRowNum PARENT ELEMENT FieldName FieldVAlue
----------- -------------- ----------------- --------------------------------------------- ---------------------
1 InternalInfo OptmInfo m_iOptStage 1
1 InternalInfo OptmInfo m_cOptTask 1098
1 InternalInfo OptmInfo m_ullAlgPmoSize 640
1 InternalInfo OptmInfo m_ullOptPmoSize 1064
1 InternalInfo OptmInfo m_ullAlgTotalTime 5328
1 InternalInfo OptmInfo m_ullAlgNetTime 2600
1 InternalInfo OptmInfo m_ullOptTotalTime 14232
1 InternalInfo OptmInfo m_ullOptNetTime 13869
1 InternalInfo OptmInfo m_fRemoteExchangePlanWouldHaveMultipleZones 0
1 EnvColl Recompile wszDb CRM_Prod
1 EnvColl Recompile wszSchema Util
1 EnvColl Recompile wszTable AreaCodeTimezone
1 EnvColl Recompile m_cRowCount 237767
1 EnvColl Recompile ullThreshold 48053
1 EnvColl Recompile wszReason heuristic
1 EnvColl Recompile m_fMissingStatsRecompile 0
1 EnvColl Recompile m_fVisibleOutsideXact 18446744073709551615
1 EnvColl Recompile m_dbId.DbIdLocal_TEMP() 14
1 EnvColl Recompile m_mdObjectId 921770341
1 EnvColl Recompile m_mdBaseIndexId 1
1 EnvColl Recompile m_cRowsetId 1
1 EnvColl Recompile m_verStats 4611686018434802649
1 EnvColl Recompile m_fAfterTriggerDelta 0
1 EnvColl Recompile m_fInsteadOfDeltaPopulate 0
1 EnvColl Recompile m_fInsteadOfDeltaInsideTrg 0
1 EnvColl Recompile m_fIsSbQueue 0
1 EnvColl Recompile m_cBricks 0
1 Recompile ModTrackingInfo wszStatName AK_AreaCodeTimezone
1 Recompile ModTrackingInfo wszColName AreaCodeExchange
1 Recompile ModTrackingInfo m_cCols 1
1 Recompile ModTrackingInfo m_idIS 1
1 Recompile ModTrackingInfo m_ullSnapShotModCtr 237767
1 Recompile ModTrackingInfo m_ullRowCount 237767
1 Recompile ModTrackingInfo ullThreshold 48053
May 27, 2017 at 9:46 am
OMG. You folks are going to actually make me smart about XML. Eirikur, that's more in the direction of where Johan was going and that's awesome. Thanks for the demo.
If I could trouble you a bit for some additional information and then I think I might have this puppy whipped, especially for performance.
1. How would I add a filter to your good code to limit the returns to "Recompile" and "ModTrackingInfo" elements?
2. Within #1 above, how would I limit the field names (and still returning the associated values) to ...
A. wszDb , wszSchema, and wszTable within the "Recompile" elements and...
B. wszStatName and wszColName within the "ModTrackingInfo" elements that are related to the "Recompile" elements?
In other words, how could I modify your good code to return my ultimate goal of...
|<FrmTbl>| |<-- From multiple "Recompile" tags -->| |<-------- From multiple "ModTrackingInfo" tags within each "Recompile" tag -------->|RowNum wszDb wszSchema wszTable wszColName wszStatName
------ -------- -------- ---------------- ---------------------- --------------------------------------------------------------
1 CRM_Prod Util AreaCodeTimezone AreaCodeExchange AK_AreaCodeTimezone
1 CRM_Prod Util AreaCodeTimezone TimezoneNumber IX_BY_TimezoneNumber_ObservesDaylightSaving_ActivityStatusCode
1 CRM_Prod Util AreaCodeTimezone ActivityStatusCode _WA_Sys_0000000B_36F11965
1 CRM_Prod Util AreaCodeTimezone ObservesDaylightSaving _WA_Sys_00000005_36F11965
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneCode AK_CRM_TimezoneEnum
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneNumber _WA_Sys_00000003_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ActivityStatusCode _WA_Sys_0000000C_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum IsDaylightSaving _WA_Sys_00000005_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ServerInDaylightSaving _WA_Sys_0000000B_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneCode AK_CRM_TimezoneEnum
1 CRM_Prod dbo CRM_TimezoneEnum TimezoneNumber _WA_Sys_00000003_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ServerInDaylightSaving _WA_Sys_0000000B_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum ActivityStatusCode _WA_Sys_0000000C_2E5BD364
1 CRM_Prod dbo CRM_TimezoneEnum IsDaylightSaving _WA_Sys_00000005_2E5BD364
2 JBMTest dbo Tally N PK_Tally_N
I think that if I can see an example of how to get there, then I can do just about anything I'll ever need XML to do for me.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 27, 2017 at 9:49 am
Jeff Moden - Friday, May 26, 2017 7:35 AMMark Cowne - Friday, May 26, 2017 5:18 AMHi Jeff,This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);OMG! Something's wrong! I actually understand what you did and it's XML! 😉 What a great example!
And I just ran your code... TOTALLY AWESOME! Thank you so much for the help. I actually understand what you did and I'm completely embarrassed at how simple you made it. It definitely shows what can be done if you know what you're doing.
Shifting gears to that very subject, I do understand everything that you're done and I recognize all of the pieces that you used to do it. I've struggled many times over many days trying to figure this out on my own. I've watched videos and have read a lot of posts and articles and still couldn't put this (apparently) simple bit of XML shredding together. I do realize that it takes practice but what on this good Green Earth did you use to train yourself in all things XML?.
I can't thank you enough for the help you've provided. You've absolutely made my day. Thanks, Mark.
Heh - Perhaps even more amazing than you understanding it, I think I may understand it. 😛 Every once in a while during a struggle with XML, I have this mind-bending epiphany and I think I understand it. Then I try to use it again and realize I didn't understand what I thought I understood. I don't know why it's so difficult for me to get - probably that I don't use it very much and never get the practice necessary to let it fully sink in.
Thanks, Mark.
May 27, 2017 at 11:17 am
Ed Wagner - Saturday, May 27, 2017 9:49 AMJeff Moden - Friday, May 26, 2017 7:35 AMMark Cowne - Friday, May 26, 2017 5:18 AMHi Jeff,This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);OMG! Something's wrong! I actually understand what you did and it's XML! 😉 What a great example!
And I just ran your code... TOTALLY AWESOME! Thank you so much for the help. I actually understand what you did and I'm completely embarrassed at how simple you made it. It definitely shows what can be done if you know what you're doing.
Shifting gears to that very subject, I do understand everything that you're done and I recognize all of the pieces that you used to do it. I've struggled many times over many days trying to figure this out on my own. I've watched videos and have read a lot of posts and articles and still couldn't put this (apparently) simple bit of XML shredding together. I do realize that it takes practice but what on this good Green Earth did you use to train yourself in all things XML?.
I can't thank you enough for the help you've provided. You've absolutely made my day. Thanks, Mark.
Heh - Perhaps even more amazing than you understanding it, I think I may understand it. 😛 Every once in a while during a struggle with XML, I have this mind-bending epiphany and I think I understand it. Then I try to use it again and realize I didn't understand what I thought I understood. I don't know why it's so difficult for me to get - probably that I don't use it very much and never get the practice necessary to let it fully sink in.
Thanks, Mark.
What's really cool is that if Eirikur can show me how to do the filtering I asked about, I might be able to figure out the necessary "pivoting" (if he doesn't go that far but would love to see it) to get to my final product, several things are going to happen.
1. I'm going to be able to solve a particularly nasty problem on my production systems and write code to keep it all up to snuff over time.
2. I'm going to understand XML a whole lot better because of the way these good folks are breaking things down. The examples they've given are incredible.
3. Since I know I'm not the only one with this nasty problem I'm having in production, I'm going to be able to help others with the problem AND, combining the info on this thread with an old thread that Matt Miller was on, I also be explain some XML in the process.
What a great thread. This is more and better than I could have possibly imagined when I first posted my question and, hopefully, we're not done with it yet.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 27, 2017 at 5:02 pm
Jeff Moden - Saturday, May 27, 2017 11:17 AMEd Wagner - Saturday, May 27, 2017 9:49 AMJeff Moden - Friday, May 26, 2017 7:35 AMMark Cowne - Friday, May 26, 2017 5:18 AMHi Jeff,This should give you what you want
WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p)
SELECT xt.RowNum
,wszDb = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)')
,wszSchema = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)')
,wszTable = StatsUsed.XMLCol.value('(../p:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)')
,wszColName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)')
,StatsName = StatsUsed.XMLCol.value('(p:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)')
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//p:ModTrackingInfo') StatsUsed (XMLCol);OMG! Something's wrong! I actually understand what you did and it's XML! 😉 What a great example!
And I just ran your code... TOTALLY AWESOME! Thank you so much for the help. I actually understand what you did and I'm completely embarrassed at how simple you made it. It definitely shows what can be done if you know what you're doing.
Shifting gears to that very subject, I do understand everything that you're done and I recognize all of the pieces that you used to do it. I've struggled many times over many days trying to figure this out on my own. I've watched videos and have read a lot of posts and articles and still couldn't put this (apparently) simple bit of XML shredding together. I do realize that it takes practice but what on this good Green Earth did you use to train yourself in all things XML?.
I can't thank you enough for the help you've provided. You've absolutely made my day. Thanks, Mark.
Heh - Perhaps even more amazing than you understanding it, I think I may understand it. 😛 Every once in a while during a struggle with XML, I have this mind-bending epiphany and I think I understand it. Then I try to use it again and realize I didn't understand what I thought I understood. I don't know why it's so difficult for me to get - probably that I don't use it very much and never get the practice necessary to let it fully sink in.
Thanks, Mark.
What's really cool is that if Eirikur can show me how to do the filtering I asked about, I might be able to figure out the necessary "pivoting" (if he doesn't go that far but would love to see it) to get to my final product, several things are going to happen.
1. I'm going to be able to solve a particularly nasty problem on my production systems and write code to keep it all up to snuff over time.
2. I'm going to understand XML a whole lot better because of the way these good folks are breaking things down. The examples they've given are incredible.
3. Since I know I'm not the only one with this nasty problem I'm having in production, I'm going to be able to help others with the problem AND, combining the info on this thread with an old thread that Matt Miller was on, I also be explain some XML in the process.What a great thread. This is more and better than I could have possibly imagined when I first posted my question and, hopefully, we're not done with it yet.
I see I am missing all sorts of fun lol.
Jeff - I think you just need a very small nudge in Eirikur's code to get where you want.
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//*:Recompile') RECOMP(DATA)
CROSS APPLY RECOMP.DATA.nodes('*:ModInfoTracking/*:Field') STU(DATA);
since the cross apply acts as a sort of "inner join", you will only get recompile nodes with ModInfoTracking nodes in it. Notice that I am not using the // so it will navigate specifically (no more "just find all instances of "Field" anywhere in the downstream tree").
----------------------------------------------------------------------------------
Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?
May 27, 2017 at 5:33 pm
Sorry one more update after scrolling up to your final request. If you want to subset the modInfoTracking fields to only what you want, you need that last cross apply to stop at ModInfoTracking.
Try this one.
SELECT xt.RowNum
--,recomp.data.query('*')
,Recomp.data.value('(*:Field[@FieldName="wszDb"])[1]/@FieldValue','NVARCHAR(128)') wszDb
,Recomp.data.value('(*:Field[@FieldName="wszSchema"])[1]/@FieldValue','NVARCHAR(128)') wszSchema
,Recomp.data.value('(*:Field[@FieldName="wszTable"])[1]/@FieldValue','NVARCHAR(128)') wszTable
,STU.data.value('(*:Field[@FieldName="wszColName"])[1]/@FieldValue','NVARCHAR(128)') wszColName
,STU.data.value('(*:Field[@FieldName="wszStatName"])[1]/@FieldValue','NVARCHAR(128)') wszColName
,STU.DATA.value('local-name(../.)','NVARCHAR(128)') AS PARENT
,STU.DATA.value('local-name(.)' ,'NVARCHAR(128)') AS ELEMENT
--,STU.DATA.value('(*:Field)[1]/@FieldName' ,'NVARCHAR(128)') AS FieldName
--,STU.DATA.value('(*:Field)[1]/@FieldValue' ,'NVARCHAR(128)') AS FieldVAlue
FROM #XMLTest xt
CROSS APPLY TheXML.nodes('//*:Recompile') RECOMP(DATA)
CROSS APPLY RECOMP.DATA.nodes('*:ModTrackingInfo') STU(DATA);
----------------------------------------------------------------------------------
Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?
Viewing 15 posts - 1 through 15 (of 34 total)
You must be logged in to reply to this topic. Login to reply