Need some help with some "XML Hell" please.

  • 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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • 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/61537
  • 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

  • Mark Cowne - Friday, May 26, 2017 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);

    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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • ALZDBA - Friday, May 26, 2017 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  🙁

    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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff Moden - Friday, May 26, 2017 7:35 AM

    Mark Cowne - Friday, May 26, 2017 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);

    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/61537
  • 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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • 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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • i

    Jeff Moden - Friday, May 26, 2017 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?

    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/61537
  • 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 output
    RowNum      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


  • 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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff Moden - Friday, May 26, 2017 7:35 AM

    Mark Cowne - Friday, May 26, 2017 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);

    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.

  • Ed Wagner - Saturday, May 27, 2017 9:49 AM

    Jeff Moden - Friday, May 26, 2017 7:35 AM

    Mark Cowne - Friday, May 26, 2017 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);

    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


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff Moden - Saturday, May 27, 2017 11:17 AM

    Ed Wagner - Saturday, May 27, 2017 9:49 AM

    Jeff Moden - Friday, May 26, 2017 7:35 AM

    Mark Cowne - Friday, May 26, 2017 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);

    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?

  • 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