xqueries_PlanCache.sql 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. -- 2013-04-13 Pedro Lopes (Microsoft) [email protected] (http://aka.ms/sqlserverteam/)
  2. --
  3. -- Plan cache xqueries
  4. --
  5. -- 2013-07-16 - Optimized xQueries performance and usability
  6. --
  7. -- 2014-03-16 - Added details to several snippets
  8. --
  9. -- Querying the plan cache for missing indexes
  10. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  11. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  12. PlanMissingIndexes AS (SELECT query_plan, cp.usecounts, cp.refcounts, cp.plan_handle
  13. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  14. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) tp
  15. WHERE cp.cacheobjtype = 'Compiled Plan'
  16. AND tp.query_plan.exist('//MissingIndex')=1
  17. )
  18. SELECT c1.value('(//MissingIndex/@Database)[1]', 'sysname') AS database_name,
  19. c1.value('(//MissingIndex/@Schema)[1]', 'sysname') AS [schema_name],
  20. c1.value('(//MissingIndex/@Table)[1]', 'sysname') AS [table_name],
  21. c1.value('@StatementText', 'VARCHAR(4000)') AS sql_text,
  22. c1.value('@StatementId', 'int') AS StatementId,
  23. pmi.usecounts,
  24. pmi.refcounts,
  25. c1.value('(//MissingIndexGroup/@Impact)[1]', 'FLOAT') AS impact,
  26. REPLACE(c1.query('for $group in //ColumnGroup for $column in $group/Column where $group/@Usage="EQUALITY" return string($column/@Name)').value('.', 'varchar(max)'),'] [', '],[') AS equality_columns,
  27. REPLACE(c1.query('for $group in //ColumnGroup for $column in $group/Column where $group/@Usage="INEQUALITY" return string($column/@Name)').value('.', 'varchar(max)'),'] [', '],[') AS inequality_columns,
  28. REPLACE(c1.query('for $group in //ColumnGroup for $column in $group/Column where $group/@Usage="INCLUDE" return string($column/@Name)').value('.', 'varchar(max)'),'] [', '],[') AS include_columns,
  29. pmi.query_plan,
  30. pmi.plan_handle
  31. FROM PlanMissingIndexes pmi
  32. CROSS APPLY pmi.query_plan.nodes('//StmtSimple') AS q1(c1)
  33. WHERE pmi.usecounts > 1
  34. ORDER BY c1.value('(//MissingIndexGroup/@Impact)[1]', 'FLOAT') DESC
  35. OPTION(RECOMPILE, MAXDOP 1);
  36. GO
  37. -- Querying the plan cache for plans that have warnings
  38. -- Note that SpillToTempDb warnings are only found in actual execution plans
  39. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  40. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  41. WarningSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, wn.query('.') AS StmtSimple, cp.plan_handle
  42. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  43. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  44. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(wn)
  45. WHERE wn.exist('//Warnings') = 1
  46. AND wn.exist('@QueryHash') = 1
  47. )
  48. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  49. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  50. c1.value('@NodeId','int') AS node_id,
  51. c1.value('@PhysicalOp','sysname') AS physical_op,
  52. c1.value('@LogicalOp','sysname') AS logical_op,
  53. CASE WHEN c2.exist('@NoJoinPredicate[. = "1"]') = 1 THEN 'NoJoinPredicate'
  54. WHEN c3.exist('@Database') = 1 THEN 'ColumnsWithNoStatistics' END AS warning,
  55. ws.objtype,
  56. ws.usecounts,
  57. ws.query_plan,
  58. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  59. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  60. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  61. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  62. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  63. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  64. ws.plan_handle
  65. FROM WarningSearch ws
  66. CROSS APPLY StmtSimple.nodes('//RelOp') AS q1(c1)
  67. CROSS APPLY c1.nodes('./Warnings') AS q2(c2)
  68. OUTER APPLY c2.nodes('./ColumnsWithNoStatistics/ColumnReference') AS q3(c3)
  69. UNION ALL
  70. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  71. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  72. c3.value('@NodeId','int') AS node_id,
  73. c3.value('@PhysicalOp','sysname') AS physical_op,
  74. c3.value('@LogicalOp','sysname') AS logical_op,
  75. CASE WHEN c2.exist('@UnmatchedIndexes[. = "1"]') = 1 THEN 'UnmatchedIndexes'
  76. WHEN (c4.exist('@ConvertIssue[. = "Cardinality Estimate"]') = 1 OR c4.exist('@ConvertIssue[. = "Seek Plan"]') = 1)
  77. THEN 'ConvertIssue_' + c4.value('@ConvertIssue','sysname') END AS warning,
  78. ws.objtype,
  79. ws.usecounts,
  80. ws.query_plan,
  81. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  82. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  83. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  84. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  85. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  86. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  87. ws.plan_handle
  88. FROM WarningSearch ws
  89. CROSS APPLY StmtSimple.nodes('//QueryPlan') AS q1(c1)
  90. CROSS APPLY c1.nodes('./Warnings') AS q2(c2)
  91. CROSS APPLY c1.nodes('./RelOp') AS q3(c3)
  92. OUTER APPLY c2.nodes('./PlanAffectingConvert') AS q4(c4)
  93. OPTION(RECOMPILE, MAXDOP 1);
  94. GO
  95. -- Querying the plan cache for batch sorts
  96. -- Do we need TF2340 or USE HINT 'DISABLE_OPTIMIZED_NESTED_LOOP' query hint?
  97. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  98. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  99. Scansearch AS (SELECT qp.query_plan, cp.usecounts, ss.query('.') AS StmtSimple, cp.plan_handle
  100. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  101. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  102. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ss)
  103. WHERE ss.exist('//RelOp[@PhysicalOp = "Nested Loops"]') = 1
  104. AND ss.exist('@QueryHash') = 1
  105. )
  106. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  107. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  108. c1.value('@NodeId','int') AS node_id,
  109. c3.value('@Database','sysname') AS database_name,
  110. c3.value('@Schema','sysname') AS [schema_name],
  111. c3.value('@Table','sysname') AS table_name,
  112. c1.value('@PhysicalOp','sysname') AS physical_operator,
  113. c1.value('@LogicalOp','sysname') AS logical_operator,
  114. c2.value('@Optimized','sysname') AS Batch_Sort_Optimized,
  115. --c2.value('@WithUnorderedPrefetch','sysname') AS WithUnorderedPrefetch,
  116. c4.value('@SerialDesiredMemory', 'int') AS MemGrant_SerialDesiredMemory,
  117. c5.value('@EstimatedAvailableMemoryGrant', 'int') AS EstimatedAvailableMemoryGrant,
  118. --c5.value('@EstimatedPagesCached', 'int') AS EstimatedPagesCached,
  119. --c5.value('@EstimatedAvailableDegreeOfParallelism', 'int') AS EstimatedAvailableDegreeOfParallelism,
  120. ss.usecounts,
  121. ss.query_plan,
  122. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  123. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  124. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  125. c1.value('@TableCardinality','sysname') AS TableCardinality,
  126. c1.value('@EstimateRows','sysname') AS EstimateRows,
  127. --c1.value('@EstimateIO','sysname') AS EstimateIO,
  128. --c1.value('@EstimateCPU','sysname') AS EstimateCPU,
  129. c1.value('@AvgRowSize','int') AS AvgRowSize,
  130. --c1.value('@Parallel','bit') AS Parallel,
  131. c1.value('@EstimateRebinds','int') AS EstimateRebinds,
  132. c1.value('@EstimateRewinds','int') AS EstimateRewinds,
  133. c1.value('@EstimatedExecutionMode','sysname') AS EstimatedExecutionMode,
  134. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  135. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  136. ss.plan_handle
  137. FROM Scansearch ss
  138. CROSS APPLY query_plan.nodes('//RelOp') AS q1(c1)
  139. CROSS APPLY c1.nodes('./NestedLoops') AS q2(c2)
  140. CROSS APPLY c1.nodes('./OutputList/ColumnReference[1]') AS q3(c3)
  141. OUTER APPLY query_plan.nodes('//MemoryGrantInfo') AS q4(c4)
  142. OUTER APPLY query_plan.nodes('//OptimizerHardwareDependentProperties') AS q5(c5)
  143. WHERE c1.exist('@PhysicalOp[. = "Nested Loops"]') = 1
  144. AND c3.value('@Schema','sysname') <> '[sys]'
  145. AND c2.value('@Optimized','sysname') = 1
  146. OPTION(RECOMPILE, MAXDOP 1);
  147. GO
  148. -- Querying the plan cache for index scans
  149. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  150. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  151. Scansearch AS (SELECT qp.query_plan, cp.usecounts, ss.query('.') AS StmtSimple, cp.plan_handle
  152. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  153. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  154. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ss)
  155. WHERE cp.cacheobjtype = 'Compiled Plan'
  156. AND (ss.exist('//RelOp[@PhysicalOp = "Index Scan"]') = 1
  157. OR ss.exist('//RelOp[@PhysicalOp = "Clustered Index Scan"]') = 1)
  158. AND ss.exist('@QueryHash') = 1
  159. )
  160. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  161. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  162. c1.value('@NodeId','int') AS node_id,
  163. c2.value('@Database','sysname') AS database_name,
  164. c2.value('@Schema','sysname') AS [schema_name],
  165. c2.value('@Table','sysname') AS table_name,
  166. c1.value('@PhysicalOp','sysname') AS physical_operator,
  167. c2.value('@Index','sysname') AS index_name,
  168. c3.value('@ScalarString[1]','VARCHAR(4000)') AS [predicate],
  169. c1.value('@TableCardinality','sysname') AS TableCardinality,
  170. c1.value('@EstimateRows','sysname') AS EstimateRows,
  171. --c1.value('@EstimateIO','sysname') AS EstimateIO,
  172. --c1.value('@EstimateCPU','sysname') AS EstimateCPU,
  173. c1.value('@AvgRowSize','int') AS AvgRowSize,
  174. --c1.value('@Parallel','bit') AS Parallel,
  175. c1.value('@EstimateRebinds','int') AS EstimateRebinds,
  176. c1.value('@EstimateRewinds','int') AS EstimateRewinds,
  177. c1.value('@EstimatedExecutionMode','sysname') AS EstimatedExecutionMode,
  178. c4.value('@Lookup','bit') AS Lookup,
  179. c4.value('@Ordered','bit') AS Ordered,
  180. c4.value('@ScanDirection','sysname') AS ScanDirection,
  181. c4.value('@ForceSeek','bit') AS ForceSeek,
  182. c4.value('@ForceScan','bit') AS ForceScan,
  183. c4.value('@NoExpandHint','bit') AS NoExpandHint,
  184. c4.value('@Storage','sysname') AS Storage,
  185. ss.usecounts,
  186. ss.query_plan,
  187. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  188. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  189. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  190. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  191. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  192. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  193. ss.plan_handle
  194. FROM Scansearch ss
  195. CROSS APPLY query_plan.nodes('//RelOp') AS q1(c1)
  196. CROSS APPLY c1.nodes('./IndexScan') AS q4(c4)
  197. CROSS APPLY c1.nodes('./IndexScan/Object') AS q2(c2)
  198. OUTER APPLY c1.nodes('./IndexScan/Predicate/ScalarOperator[1]') AS q3(c3)
  199. WHERE (c1.exist('@PhysicalOp[. = "Index Scan"]') = 1
  200. OR c1.exist('@PhysicalOp[. = "Clustered Index Scan"]') = 1)
  201. AND c2.value('@Schema','sysname') <> '[sys]'
  202. OPTION(RECOMPILE, MAXDOP 1);
  203. GO
  204. -- Querying the plan cache for Lookups
  205. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  206. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  207. Lookupsearch AS (SELECT qp.query_plan, cp.usecounts, ls.query('.') AS StmtSimple, cp.plan_handle
  208. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  209. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  210. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ls)
  211. WHERE cp.cacheobjtype = 'Compiled Plan'
  212. AND ls.exist('//IndexScan[@Lookup = "1"]') = 1
  213. AND ls.exist('@QueryHash') = 1
  214. )
  215. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  216. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  217. c1.value('@NodeId','int') AS node_id,
  218. c2.value('@Database','sysname') AS database_name,
  219. c2.value('@Schema','sysname') AS [schema_name],
  220. c2.value('@Table','sysname') AS table_name,
  221. 'Lookup - ' + c1.value('@PhysicalOp','sysname') AS physical_operator,
  222. c2.value('@Index','sysname') AS index_name,
  223. c3.value('@ScalarString','VARCHAR(4000)') AS predicate,
  224. c1.value('@TableCardinality','sysname') AS table_cardinality,
  225. c1.value('@EstimateRows','sysname') AS estimate_rows,
  226. c1.value('@AvgRowSize','sysname') AS avg_row_size,
  227. ls.usecounts,
  228. ls.query_plan,
  229. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  230. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  231. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  232. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  233. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  234. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  235. ls.plan_handle
  236. FROM Lookupsearch ls
  237. CROSS APPLY query_plan.nodes('//RelOp') AS q1(c1)
  238. CROSS APPLY c1.nodes('./IndexScan/Object') AS q2(c2)
  239. OUTER APPLY c1.nodes('./IndexScan//ScalarOperator[1]') AS q3(c3)
  240. -- Below attribute is present either in Index Seeks or RID Lookups so it can reveal a Lookup is executed
  241. WHERE c1.exist('./IndexScan[@Lookup = "1"]') = 1
  242. AND c2.value('@Schema','sysname') <> '[sys]'
  243. OPTION(RECOMPILE, MAXDOP 1);
  244. GO
  245. -- Querying the plan cache for specific Implicit type conversions
  246. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  247. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  248. Convertsearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, cp.plan_handle, cs.query('.') AS StmtSimple
  249. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  250. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  251. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(cs)
  252. WHERE cp.cacheobjtype = 'Compiled Plan'
  253. AND cs.exist('@QueryHash') = 1
  254. AND cs.exist('.//ScalarOperator[contains(@ScalarString, "CONVERT_IMPLICIT")]') = 1
  255. AND cs.exist('.[contains(@StatementText, "Convertsearch")]') = 0
  256. )
  257. SELECT c2.value('@StatementText', 'VARCHAR(4000)') AS sql_text,
  258. c2.value('@StatementId', 'int') AS StatementId,
  259. c3.value('@ScalarString[1]','VARCHAR(4000)') AS expression,
  260. ss.usecounts,
  261. ss.query_plan,
  262. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  263. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  264. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  265. c2.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  266. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  267. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  268. ss.plan_handle
  269. FROM Convertsearch ss
  270. CROSS APPLY query_plan.nodes('//StmtSimple') AS q2(c2)
  271. CROSS APPLY c2.nodes('.//ScalarOperator[contains(@ScalarString, "CONVERT_IMPLICIT")]') AS q3(c3)
  272. OPTION(RECOMPILE, MAXDOP 1);
  273. GO
  274. -- Querying the plan cache for index usage (change @IndexName below)
  275. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  276. DECLARE @IndexName sysname = '<ix_name>';
  277. SET @IndexName = QUOTENAME(@IndexName,'[');
  278. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  279. IndexSearch AS (SELECT qp.query_plan, cp.usecounts, ix.query('.') AS StmtSimple, cp.plan_handle
  280. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  281. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  282. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ix)
  283. WHERE cp.cacheobjtype = 'Compiled Plan'
  284. AND ix.exist('//Object[@Index = sql:variable("@IndexName")]') = 1
  285. )
  286. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  287. c2.value('@Database','sysname') AS database_name,
  288. c2.value('@Schema','sysname') AS [schema_name],
  289. c2.value('@Table','sysname') AS table_name,
  290. c2.value('@Index','sysname') AS index_name,
  291. c1.value('@PhysicalOp','NVARCHAR(50)') as physical_operator,
  292. c3.value('@ScalarString[1]','VARCHAR(4000)') AS predicate,
  293. c4.value('@Column[1]','VARCHAR(256)') AS seek_columns,
  294. c1.value('@EstimateRows','sysname') AS estimate_rows,
  295. c1.value('@AvgRowSize','sysname') AS avg_row_size,
  296. ixs.query_plan,
  297. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  298. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  299. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  300. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  301. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  302. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  303. ixs.plan_handle
  304. FROM IndexSearch ixs
  305. CROSS APPLY StmtSimple.nodes('//RelOp') AS q1(c1)
  306. CROSS APPLY c1.nodes('IndexScan/Object[@Index = sql:variable("@IndexName")]') AS q2(c2)
  307. OUTER APPLY c1.nodes('IndexScan/Predicate/ScalarOperator') AS q3(c3)
  308. OUTER APPLY c1.nodes('IndexScan/SeekPredicates/SeekPredicateNew//ColumnReference') AS q4(c4)
  309. OPTION(RECOMPILE, MAXDOP 1);
  310. GO
  311. -- Querying the plan cache for parametrization
  312. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  313. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  314. PlanParameters AS (SELECT cp.plan_handle, qp.query_plan, qp.dbid, qp.objectid
  315. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  316. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  317. WHERE qp.query_plan.exist('//ParameterList')=1
  318. AND cp.cacheobjtype = 'Compiled Plan'
  319. )
  320. SELECT QUOTENAME(DB_NAME(pp.dbid)) AS database_name,
  321. ISNULL(OBJECT_NAME(pp.objectid, pp.dbid), 'No_Associated_Object') AS [object_name],
  322. c2.value('(@Column)[1]','sysname') AS parameter_name,
  323. c2.value('(@ParameterCompiledValue)[1]','VARCHAR(max)') AS parameter_compiled_value,
  324. pp.query_plan,
  325. pp.plan_handle
  326. FROM PlanParameters pp
  327. CROSS APPLY query_plan.nodes('//ParameterList') AS q1(c1)
  328. CROSS APPLY c1.nodes('ColumnReference') as q2(c2)
  329. WHERE pp.dbid > 4 AND pp.dbid < 32767
  330. OPTION(RECOMPILE, MAXDOP 1);
  331. GO
  332. -- Querying the plan cache for plans that use parallelism and their cost (useful for tuning Cost Threshold for Parallelism)
  333. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  334. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  335. ParallelSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, ix.query('.') AS StmtSimple, cp.plan_handle
  336. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  337. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  338. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ix)
  339. WHERE ix.exist('//RelOp[@Parallel = "1"]') = 1
  340. AND ix.exist('@QueryHash') = 1
  341. )
  342. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  343. ps.plan_handle,
  344. ps.objtype,
  345. ps.usecounts,
  346. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  347. ps.query_plan,
  348. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  349. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  350. c1.value('@CachedPlanSize','sysname') AS CachedPlanSize,
  351. c2.value('@SerialRequiredMemory','sysname') AS SerialRequiredMemory,
  352. c2.value('@SerialDesiredMemory','sysname') AS SerialDesiredMemory,
  353. c3.value('@EstimatedAvailableMemoryGrant','sysname') AS EstimatedAvailableMemoryGrant,
  354. c3.value('@EstimatedPagesCached','sysname') AS EstimatedPagesCached,
  355. c3.value('@EstimatedAvailableDegreeOfParallelism','sysname') AS EstimatedAvailableDegreeOfParallelism,
  356. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  357. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash
  358. FROM ParallelSearch ps
  359. CROSS APPLY StmtSimple.nodes('//QueryPlan') AS q1(c1)
  360. CROSS APPLY c1.nodes('.//MemoryGrantInfo') AS q2(c2)
  361. CROSS APPLY c1.nodes('.//OptimizerHardwareDependentProperties') AS q3(c3)
  362. OPTION(RECOMPILE, MAXDOP 1);
  363. GO
  364. -- Querying the plan cache for plans that use parallelism, with more details
  365. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  366. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  367. ParallelSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, ix.query('.') AS StmtSimple, cp.plan_handle
  368. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  369. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  370. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ix)
  371. WHERE cp.cacheobjtype = 'Compiled Plan'
  372. AND ix.exist('//RelOp[@Parallel = "1"]') = 1
  373. AND ix.exist('@QueryHash') = 1
  374. )
  375. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  376. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  377. c1.value('@NodeId','int') AS node_id,
  378. c2.value('@Database','sysname') AS database_name,
  379. c2.value('@Schema','sysname') AS [schema_name],
  380. c2.value('@Table','sysname') AS table_name,
  381. c2.value('@Index','sysname') AS [index],
  382. c2.value('@IndexKind','sysname') AS index_type,
  383. c1.value('@PhysicalOp','sysname') AS physical_op,
  384. c1.value('@LogicalOp','sysname') AS logical_op,
  385. c1.value('@TableCardinality','sysname') AS table_cardinality,
  386. c1.value('@EstimateRows','sysname') AS estimate_rows,
  387. c1.value('@AvgRowSize','sysname') AS avg_row_size,
  388. ps.objtype,
  389. ps.usecounts,
  390. ps.query_plan,
  391. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  392. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  393. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  394. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  395. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  396. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  397. ps.plan_handle
  398. FROM ParallelSearch ps
  399. CROSS APPLY StmtSimple.nodes('//Parallelism//RelOp') AS q1(c1)
  400. CROSS APPLY c1.nodes('.//IndexScan/Object') AS q2(c2)
  401. WHERE c1.value('@Parallel','int') = 1
  402. AND (c1.exist('@PhysicalOp[. = "Index Scan"]') = 1
  403. OR c1.exist('@PhysicalOp[. = "Clustered Index Scan"]') = 1
  404. OR c1.exist('@PhysicalOp[. = "Index Seek"]') = 1
  405. OR c1.exist('@PhysicalOp[. = "Clustered Index Seek"]') = 1
  406. OR c1.exist('@PhysicalOp[. = "Table Scan"]') = 1)
  407. AND c2.value('@Schema','sysname') <> '[sys]'
  408. OPTION(RECOMPILE, MAXDOP 1);
  409. GO
  410. -- Querying the plan cache for plans that use parallelism, and scheduler time < elapsed time
  411. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  412. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  413. ParallelSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, qs.[total_worker_time], qs.[total_elapsed_time], qs.[execution_count],
  414. ix.query('.') AS StmtSimple, cp.plan_handle
  415. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  416. INNER JOIN sys.dm_exec_query_stats qs (NOLOCK) ON cp.plan_handle = qs.plan_handle
  417. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  418. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ix)
  419. WHERE cp.cacheobjtype = 'Compiled Plan'
  420. AND ix.exist('//RelOp[@Parallel = "1"]') = 1
  421. AND ix.exist('@QueryHash') = 1
  422. AND (qs.[total_worker_time]/qs.[execution_count]) < (qs.[total_elapsed_time]/qs.[execution_count])
  423. )
  424. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  425. ps.objtype,
  426. ps.usecounts,
  427. ps.[total_worker_time]/ps.[execution_count] AS avg_worker_time,
  428. ps.[total_elapsed_time]/ps.[execution_count] As avg_elapsed_time,
  429. ps.query_plan,
  430. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  431. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  432. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  433. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  434. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  435. ps.plan_handle
  436. FROM ParallelSearch ps
  437. CROSS APPLY StmtSimple.nodes('//RelOp[1]') AS q1(c1)
  438. WHERE c1.value('@Parallel','int') = 1 AND c1.value('@NodeId','int') = 0
  439. OPTION(RECOMPILE, MAXDOP 1);
  440. GO
  441. -- Querying the plan cache for plans that use parallelism, and scheduler time < elapsed time and more detailed output
  442. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  443. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  444. ParallelSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, qs.[total_worker_time], qs.[total_elapsed_time], qs.[execution_count],
  445. ix.query('.') AS StmtSimple, cp.plan_handle
  446. FROM sys.dm_exec_cached_plans cp (NOLOCK)
  447. INNER JOIN sys.dm_exec_query_stats qs (NOLOCK) ON cp.plan_handle = qs.plan_handle
  448. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  449. CROSS APPLY qp.query_plan.nodes('//StmtSimple') AS p(ix)
  450. WHERE cp.cacheobjtype = 'Compiled Plan'
  451. AND ix.exist('//RelOp[@Parallel = "1"]') = 1
  452. AND ix.exist('@QueryHash') = 1
  453. AND (qs.[total_worker_time]/qs.[execution_count]) < (qs.[total_elapsed_time]/qs.[execution_count])
  454. )
  455. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  456. StmtSimple.value('StmtSimple[1]/@StatementId', 'int') AS StatementId,
  457. c1.value('@NodeId','int') AS node_id,
  458. c2.value('@Database','sysname') AS database_name,
  459. c2.value('@Schema','sysname') AS [schema_name],
  460. c2.value('@Table','sysname') AS table_name,
  461. c2.value('@Index','sysname') AS [index],
  462. c2.value('@IndexKind','sysname') AS index_type,
  463. c1.value('@PhysicalOp','sysname') AS physical_op,
  464. c1.value('@LogicalOp','sysname') AS logical_op,
  465. c1.value('@TableCardinality','sysname') AS table_cardinality,
  466. c1.value('@EstimateRows','sysname') AS estimate_rows,
  467. c1.value('@AvgRowSize','sysname') AS avg_row_size,
  468. ps.objtype,
  469. ps.usecounts,
  470. ps.[total_worker_time]/ps.[execution_count] AS avg_worker_time,
  471. ps.[total_elapsed_time]/ps.[execution_count] As avg_elapsed_time,
  472. ps.query_plan,
  473. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  474. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  475. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  476. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  477. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  478. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  479. ps.plan_handle
  480. FROM ParallelSearch ps
  481. CROSS APPLY StmtSimple.nodes('//Parallelism//RelOp') AS q1(c1)
  482. OUTER APPLY c1.nodes('.//IndexScan/Object') AS q2(c2)
  483. WHERE c1.value('@Parallel','int') = 1
  484. AND (c1.exist('@PhysicalOp[. = "Index Scan"]') = 1
  485. OR c1.exist('@PhysicalOp[. = "Clustered Index Scan"]') = 1
  486. OR c1.exist('@PhysicalOp[. = "Index Seek"]') = 1
  487. OR c1.exist('@PhysicalOp[. = "Clustered Index Seek"]') = 1
  488. OR c1.exist('@PhysicalOp[. = "Table Scan"]') = 1)
  489. AND c2.value('@Schema','sysname') <> '[sys]'
  490. OPTION(RECOMPILE, MAXDOP 1);
  491. GO
  492. -- Querying the plan cache for specific statements (change @Statement below)
  493. DECLARE @Statement VARCHAR(4000) = 'Sales.SalesOrderDetail';
  494. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  495. WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
  496. StatementSearch AS (SELECT qp.query_plan, cp.usecounts, cp.objtype, cp.plan_handle, ss.query('.') AS StmtSimple
  497. FROM sys.dm_exec_cached_plans cp
  498. CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
  499. CROSS APPLY query_plan.nodes('//StmtSimple[contains(@StatementText, sql:variable("@Statement"))]') AS p(ss)
  500. WHERE cp.cacheobjtype = 'Compiled Plan'
  501. AND ss.exist('@QueryHash') = 1
  502. )
  503. SELECT StmtSimple.value('StmtSimple[1]/@StatementText', 'VARCHAR(4000)') AS sql_text,
  504. ss.objtype,
  505. ss.usecounts,
  506. ss.query_plan,
  507. StmtSimple.value('StmtSimple[1]/@QueryHash', 'VARCHAR(100)') AS query_hash,
  508. StmtSimple.value('StmtSimple[1]/@QueryPlanHash', 'VARCHAR(100)') AS query_plan_hash,
  509. StmtSimple.value('StmtSimple[1]/@StatementSubTreeCost', 'sysname') AS StatementSubTreeCost,
  510. c1.value('@EstimatedTotalSubtreeCost','sysname') AS EstimatedTotalSubtreeCost,
  511. StmtSimple.value('StmtSimple[1]/@StatementOptmEarlyAbortReason', 'sysname') AS StatementOptmEarlyAbortReason,
  512. StmtSimple.value('StmtSimple[1]/@StatementOptmLevel', 'sysname') AS StatementOptmLevel,
  513. ss.plan_handle
  514. FROM StatementSearch ss
  515. CROSS APPLY StmtSimple.nodes('//Parallelism//RelOp') AS q1(c1)
  516. OPTION(RECOMPILE, MAXDOP 1);
  517. GO