Understanding which SQL Server queries consume the most CPU resources is crucial for database performance tuning. However, focusing solely on individual statements might not always provide the complete picture. Instead, examining queries at a batch or stored procedure level offers a broader view, enabling more effective optimizations. This approach aggregates CPU usage across all statements within a batch or stored procedure, facilitating a targeted investigation of high-impact areas.
Overview
The goal here is to aggregate CPU usage by all statements within the same batch or stored procedure, identified by their shared 'plan_handle'. This high-level view helps in pinpointing which queries are most costly in terms of CPU resources. Later on, we can drill down to individual statements with the highest CPU consumption for detailed analysis.
IF OBJECT_ID('tempdb..#top_costly_queries') IS NOT NULL DROP TABLE #top_costly_queries; GO ;WITH cte AS ( SELECT TOP 5 SUM(qs.total_worker_time) AS total_cpu_time, SUM(qs.execution_count) AS total_execution_count, SUM(qs.total_worker_time) / SUM(qs.execution_count) AS avg_cpu_time, SUM(qs.total_elapsed_time) / SUM(qs.execution_count) AS avg_elapsed_time, COUNT(*) AS number_of_statements, qs.plan_handle FROM sys.dm_exec_que;ry_stats AS qs GROUP BY qs.plan_handle HAVING SUM(qs.execution_count) > 1 ) SELECT DB_NAME(qp.dbid) AS db_name, COALESCE(QUOTENAME(DB_NAME(qp.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(qp.objectid, qp.dbid)) + N'.' + QUOTENAME(OBJECT_NAME(qp.objectid, qp.dbid)), '') AS obj_name, qp.objectid, qp.query_plan, cte.* INTO #top_costly_queries FROM cte CROSS APPLY sys.dm_exec_query_plan(cte.plan_handle) AS qp ORDER BY total_cpu_time DESC;
Analyzing the Results
After identifying the batches or stored procedures with the highest CPU usage, we focus on the specific statements within them:
SELECT t.*, SUBSTRING(st.text, ( qs.statement_start_offset / 2 ) + 1, ( (CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset ) / 2 ) + 1) AS statement_text, qs.total_worker_time / qs.execution_count AS avg_cpu_time_stmt, qs.*, st.* FROM sys.dm_exec_query_stats AS qs INNER JOIN #top_costly_queries AS t ON t.plan_handle = qs.plan_handle CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st ORDER BY qs.total_worker_time DESC;
Partial Results:
This detailed examination helps pinpoint specific statements within the identified batches or stored procedures that warrant further investigation due to their high CPU usage.
Conclusion