|
1 | 1 | /********************************************************************************************* |
2 | | -sp_WhatsupQueryStore v1.1 |
3 | | -(C) Enrico van de Laar (@evdlaar) |
| 2 | +sp_WhatsupQueryStore v1.2 |
| 3 | +(C) Enrico van de Laar (Twitter: @evdlaar) |
| 4 | +https://github.com/Evdlaar/sp_WhatsupQueryStore |
4 | 5 |
|
5 | | -Feedback: mailto:enrico@dotnine.net |
| 6 | +Feedback: mailto:enrico@dotnine.net |
6 | 7 |
|
7 | 8 | License: |
8 | 9 | This script is free to download and use for personal, educational, and internal |
@@ -32,13 +33,17 @@ CREATE PROCEDURE sp_WhatsupQueryStore |
32 | 33 | @dbname VARCHAR(75), |
33 | 34 | @timewindow VARCHAR(4) = 1, |
34 | 35 | @topqueries VARCHAR(4) = 25, |
35 | | - @return_store_config INT = 1, |
36 | | - @return_forced_plans INT = 1, |
37 | | - @return_top_duration INT = 1, |
38 | | - @return_top_cpu INT = 1, |
39 | | - @return_top_log_read INT = 1, |
40 | | - @return_top_log_write INT = 1, |
41 | | - @return_top_phys_read INT = 1 |
| 36 | + @return_store_config INT = 0, |
| 37 | + @return_forced_plans INT = 0, |
| 38 | + @return_multiple_plans INT = 0, |
| 39 | + @return_top_executed INT = 0, |
| 40 | + @return_top_duration INT = 0, |
| 41 | + @return_top_cpu INT = 0, |
| 42 | + @return_top_log_read INT = 0, |
| 43 | + @return_top_log_write INT = 0, |
| 44 | + @return_top_phys_read INT = 0, |
| 45 | + @return_all INT = 0, |
| 46 | + @show_query_hints INT = 0 |
42 | 47 |
|
43 | 48 | AS |
44 | 49 |
|
|
75 | 80 | -- Now that we detected that the Query Store is enabled on the target database |
76 | 81 | -- we can start grabbing information from it. |
77 | 82 |
|
78 | | - IF @return_store_config = 1 |
| 83 | + IF @return_store_config = 1 OR @return_all = 1 |
79 | 84 |
|
80 | 85 | BEGIN |
81 | 86 |
|
|
113 | 118 | Forced Query Store plans |
114 | 119 | */ |
115 | 120 |
|
116 | | - IF @return_forced_plans = 1 |
| 121 | + IF @return_forced_plans = 1 OR @return_all = 1 |
117 | 122 | BEGIN |
118 | 123 |
|
119 | 124 | SELECT 'Forced Query Store Execution Plans' AS 'Info' |
@@ -146,85 +151,148 @@ ELSE |
146 | 151 | Queries with multiple execution plans |
147 | 152 | */ |
148 | 153 |
|
149 | | - SELECT 'Queries with multiple plans in the last ' + @timewindow + ' hour(s)' AS 'Info' |
150 | | - |
151 | | - DECLARE @sql_qs_multiple_plans NVARCHAR(MAX) |
152 | | - |
153 | | - SET @sql_qs_multiple_plans = |
154 | | - N'WITH CTE_QS_Multiple_Plans (query_id, plan_count) |
155 | | - AS |
156 | | - ( |
157 | | - SELECT |
158 | | - qsp.query_id, |
159 | | - COUNT(qsp.plan_id) |
160 | | - FROM ' + @dbname + '.sys.query_store_plan qsp WITH (NOLOCK) |
161 | | - WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
162 | | - GROUP BY query_id HAVING COUNT(plan_id) > 1 |
163 | | - ) |
164 | | -
|
165 | | - SELECT |
166 | | - cte.query_id AS ''Query ID'', |
167 | | - CAST(qsp.query_plan AS XML) AS ''Execution Plan'' |
168 | | - FROM CTE_QS_Multiple_Plans cte |
169 | | - INNER JOIN ' + @dbname + '.sys.query_store_plan qsp |
170 | | - ON cte.query_id = qsp.query_id |
171 | | - INNER JOIN ' + @dbname + '.sys.query_store_query qsq WITH (NOLOCK) |
172 | | - ON qsp.query_id = qsq.query_id |
173 | | - INNER JOIN ' + @dbname + '.sys.query_store_query_text qsqt WITH (NOLOCK) |
174 | | - ON qsq.query_text_id = qsqt.query_text_id |
175 | | - WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
176 | | - ORDER BY cte.query_id ASC |
177 | | - ' |
178 | | - |
179 | | - EXEC sp_executesql @sql_qs_multiple_plans |
| 154 | + IF @return_multiple_plans = 1 OR @return_all = 1 |
| 155 | + |
| 156 | + BEGIN |
| 157 | + |
| 158 | + SELECT 'Queries with multiple plans in the last ' + @timewindow + ' hour(s)' AS 'Info' |
| 159 | + |
| 160 | + DECLARE @sql_qs_multiple_plans NVARCHAR(MAX) |
| 161 | + |
| 162 | + IF @show_query_hints = 0 |
| 163 | + |
| 164 | + BEGIN |
| 165 | + |
| 166 | + SET @sql_qs_multiple_plans = |
| 167 | + N'WITH CTE_QS_Multiple_Plans (query_id, plan_count) |
| 168 | + AS |
| 169 | + ( |
| 170 | + SELECT |
| 171 | + qsp.query_id, |
| 172 | + COUNT(qsp.plan_id) |
| 173 | + FROM ' + @dbname + '.sys.query_store_plan qsp WITH (NOLOCK) |
| 174 | + WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
| 175 | + GROUP BY query_id HAVING COUNT(plan_id) > 1 |
| 176 | + ) |
| 177 | +
|
| 178 | + SELECT |
| 179 | + cte.query_id AS ''Query ID'', |
| 180 | + qsp.plan_id AS ''Plan ID'', |
| 181 | + CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) AS ''Last Execution Time'', |
| 182 | + qsqt.query_sql_text AS ''Query Text'', |
| 183 | + qsp.engine_version AS ''Engine Version'', |
| 184 | + qsp.[compatibility_level] AS ''Compatibility Level'', |
| 185 | + CAST(qsp.query_plan AS XML) AS ''Execution Plan'' |
| 186 | + FROM CTE_QS_Multiple_Plans cte |
| 187 | + INNER JOIN ' + @dbname + '.sys.query_store_plan qsp |
| 188 | + ON cte.query_id = qsp.query_id |
| 189 | + INNER JOIN ' + @dbname + '.sys.query_store_query qsq WITH (NOLOCK) |
| 190 | + ON qsp.query_id = qsq.query_id |
| 191 | + INNER JOIN ' + @dbname + '.sys.query_store_query_text qsqt WITH (NOLOCK) |
| 192 | + ON qsq.query_text_id = qsqt.query_text_id |
| 193 | + WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
| 194 | + ORDER BY cte.query_id ASC |
| 195 | + ' |
| 196 | + |
| 197 | + EXEC sp_executesql @sql_qs_multiple_plans |
| 198 | + |
| 199 | + END |
| 200 | + |
| 201 | + IF @show_query_hints = 1 |
| 202 | + |
| 203 | + BEGIN |
| 204 | + |
| 205 | + SET @sql_qs_multiple_plans = |
| 206 | + N'WITH CTE_QS_Multiple_Plans (query_id, plan_count) |
| 207 | + AS |
| 208 | + ( |
| 209 | + SELECT |
| 210 | + qsp.query_id, |
| 211 | + COUNT(qsp.plan_id) |
| 212 | + FROM ' + @dbname + '.sys.query_store_plan qsp WITH (NOLOCK) |
| 213 | + WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
| 214 | + GROUP BY query_id HAVING COUNT(plan_id) > 1 |
| 215 | + ) |
| 216 | +
|
| 217 | + SELECT |
| 218 | + cte.query_id AS ''Query ID'', |
| 219 | + qsp.plan_id AS ''Plan ID'', |
| 220 | + CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) AS ''Last Execution Time'', |
| 221 | + qsqt.query_sql_text AS ''Query Text'', |
| 222 | + qsp.engine_version AS ''Engine Version'', |
| 223 | + qsp.[compatibility_level] AS ''Compatibility Level'', |
| 224 | + CAST(qsp.query_plan AS XML) AS ''Execution Plan'', |
| 225 | + CAST(''-- Force Execution Plan '' + CAST(qsp.plan_id AS varchar) + '' for Query '' + CAST(cte.query_id AS varchar) + '' through the Query Store 
 EXEC sp_query_store_force_plan '' + CAST(cte.query_id AS varchar) + '', '' + CAST(qsp.plan_id AS varchar) + '''' AS XML) AS ''Force Plan'' |
| 226 | + FROM CTE_QS_Multiple_Plans cte |
| 227 | + INNER JOIN ' + @dbname + '.sys.query_store_plan qsp |
| 228 | + ON cte.query_id = qsp.query_id |
| 229 | + INNER JOIN ' + @dbname + '.sys.query_store_query qsq WITH (NOLOCK) |
| 230 | + ON qsp.query_id = qsq.query_id |
| 231 | + INNER JOIN ' + @dbname + '.sys.query_store_query_text qsqt WITH (NOLOCK) |
| 232 | + ON qsq.query_text_id = qsqt.query_text_id |
| 233 | + WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsp.last_execution_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
| 234 | + ORDER BY cte.query_id ASC |
| 235 | + ' |
| 236 | + |
| 237 | + EXEC sp_executesql @sql_qs_multiple_plans |
| 238 | + |
| 239 | + END |
| 240 | + |
| 241 | + END |
180 | 242 |
|
181 | 243 | /* |
182 | 244 | SECTION 4 |
183 | 245 | Top queries based on execution amount |
184 | 246 | */ |
185 | 247 |
|
186 | | - SELECT 'Most executed queries in the last ' + @timewindow + ' hour(s)' AS 'Info' |
187 | | - |
188 | | - DECLARE @sql_qs_top_nr_executions NVARCHAR(MAX) |
189 | | - |
190 | | - SET @sql_qs_top_nr_executions = |
191 | | - N'WITH CTE_QS_Top_Executions (plan_id, exec_count) |
192 | | - AS |
193 | | - ( |
194 | | - SELECT TOP ' + @topqueries + ' |
195 | | - qsrs.plan_id, |
196 | | - SUM(qsrs.count_executions) |
197 | | - FROM ' + @dbname + '.sys.query_store_runtime_stats qsrs WITH (NOLOCK) |
198 | | - INNER JOIN ' + @dbname + '.sys.query_store_runtime_stats_interval qsrsi WITH (NOLOCK) |
199 | | - ON qsrs.runtime_stats_interval_id = qsrsi.runtime_stats_interval_id |
200 | | - WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsrsi.end_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
201 | | - GROUP BY qsrs.plan_id |
202 | | - ORDER BY SUM(qsrs.count_executions) DESC |
203 | | - ) |
204 | | -
|
205 | | - SELECT |
206 | | - cte.plan_id AS ''Plan ID'', |
207 | | - qsp.query_id AS ''Query ID'', |
208 | | - qsqt.query_sql_text AS ''Statement'', |
209 | | - CAST(qsp.query_plan AS XML) AS ''Execution Plan'', |
210 | | - cte.exec_count AS ''Execution count'' |
211 | | - FROM CTE_QS_Top_Executions cte |
212 | | - INNER JOIN ' + @dbname + '.sys.query_store_plan qsp WITH (NOLOCK) |
213 | | - ON cte.plan_id = qsp.plan_id |
214 | | - INNER JOIN ' + @dbname + '.sys.query_store_query qsq WITH (NOLOCK) |
215 | | - ON qsp.query_id = qsq.query_id |
216 | | - INNER JOIN ' + @dbname + '.sys.query_store_query_text qsqt WITH (NOLOCK) |
217 | | - ON qsq.query_text_id = qsqt.query_text_id |
218 | | - ' |
| 248 | + IF @return_top_executed = 1 OR @return_all = 1 |
| 249 | + |
| 250 | + BEGIN |
| 251 | + |
| 252 | + SELECT 'Most executed queries in the last ' + @timewindow + ' hour(s)' AS 'Info' |
| 253 | + |
| 254 | + DECLARE @sql_qs_top_nr_executions NVARCHAR(MAX) |
| 255 | + |
| 256 | + SET @sql_qs_top_nr_executions = |
| 257 | + N'WITH CTE_QS_Top_Executions (plan_id, exec_count) |
| 258 | + AS |
| 259 | + ( |
| 260 | + SELECT TOP ' + @topqueries + ' |
| 261 | + qsrs.plan_id, |
| 262 | + SUM(qsrs.count_executions) |
| 263 | + FROM ' + @dbname + '.sys.query_store_runtime_stats qsrs WITH (NOLOCK) |
| 264 | + INNER JOIN ' + @dbname + '.sys.query_store_runtime_stats_interval qsrsi WITH (NOLOCK) |
| 265 | + ON qsrs.runtime_stats_interval_id = qsrsi.runtime_stats_interval_id |
| 266 | + WHERE CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset, qsrsi.end_time), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) >= DATEADD(hour, -' + @timewindow + ', getdate()) |
| 267 | + GROUP BY qsrs.plan_id |
| 268 | + ORDER BY SUM(qsrs.count_executions) DESC |
| 269 | + ) |
| 270 | +
|
| 271 | + SELECT |
| 272 | + cte.plan_id AS ''Plan ID'', |
| 273 | + qsp.query_id AS ''Query ID'', |
| 274 | + qsqt.query_sql_text AS ''Statement'', |
| 275 | + CAST(qsp.query_plan AS XML) AS ''Execution Plan'', |
| 276 | + cte.exec_count AS ''Execution count'' |
| 277 | + FROM CTE_QS_Top_Executions cte |
| 278 | + INNER JOIN ' + @dbname + '.sys.query_store_plan qsp WITH (NOLOCK) |
| 279 | + ON cte.plan_id = qsp.plan_id |
| 280 | + INNER JOIN ' + @dbname + '.sys.query_store_query qsq WITH (NOLOCK) |
| 281 | + ON qsp.query_id = qsq.query_id |
| 282 | + INNER JOIN ' + @dbname + '.sys.query_store_query_text qsqt WITH (NOLOCK) |
| 283 | + ON qsq.query_text_id = qsqt.query_text_id |
| 284 | + ' |
219 | 285 |
|
220 | | - EXEC sp_executesql @sql_qs_top_nr_executions |
| 286 | + EXEC sp_executesql @sql_qs_top_nr_executions |
| 287 | + |
| 288 | + END |
221 | 289 |
|
222 | 290 | /* |
223 | 291 | SECTION 5 |
224 | 292 | Top queries based on average duration |
225 | 293 | */ |
226 | 294 |
|
227 | | - IF @return_top_duration = 1 |
| 295 | + IF @return_top_duration = 1 OR @return_all = 1 |
228 | 296 |
|
229 | 297 | BEGIN |
230 | 298 |
|
|
277 | 345 | Top queries based on average CPU Time |
278 | 346 | */ |
279 | 347 |
|
280 | | - IF @return_top_cpu = 1 |
| 348 | + IF @return_top_cpu = 1 OR @return_all = 1 |
281 | 349 |
|
282 | 350 | BEGIN |
283 | 351 |
|
|
330 | 398 | Top queries based on average logical IO reads |
331 | 399 | */ |
332 | 400 |
|
333 | | - IF @return_top_log_read = 1 |
| 401 | + IF @return_top_log_read = 1 OR @return_all = 1 |
334 | 402 |
|
335 | 403 | BEGIN |
336 | 404 |
|
|
383 | 451 | Top queries based on average logical IO writes |
384 | 452 | */ |
385 | 453 |
|
386 | | - IF @return_top_log_write = 1 |
| 454 | + IF @return_top_log_write = 1 OR @return_all = 1 |
387 | 455 |
|
388 | 456 | BEGIN |
389 | 457 |
|
|
436 | 504 | Top queries based on average physical IO reads |
437 | 505 | */ |
438 | 506 |
|
439 | | - IF @return_top_phys_read = 1 |
| 507 | + IF @return_top_phys_read = 1 OR @return_all = 1 |
440 | 508 |
|
441 | 509 | BEGIN |
442 | 510 |
|
|
0 commit comments