/***** Script: Top 10 queries that causes high CPU Utilization *****/
/***** Support: SQL Server 2008 and Above *****/
/***** TestedOn: SQL Server 2008,R2 and 2014 *****/
/***** Output: All query related details *****/
/***** Note: This script returns list of costly queries when CPU utilization is >=80% from last 10 min ****/
SET NOCOUNT ON
DECLARE @ts_now bigint
DECLARE @AvgCPUUtilization DECIMAL(10,2)
SELECT @ts_now = cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info
-- load the CPU utilization in the past 10 minutes into the temp table, you can load them into a permanent table
SELECT TOP(10) SQLProcessUtilization AS [SQLServerProcessCPUUtilization]
,SystemIdle AS [SystemIdleProcess]
,100 - SystemIdle - SQLProcessUtilization AS [OtherProcessCPU Utilization]
,DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [EventTime]
INTO #CPUUtilization
FROM (
SELECT record.value('(./Record/@id)[1]', 'int') AS record_id,
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int')
AS [SystemIdle],
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]',
'int')
AS [SQLProcessUtilization], [timestamp]
FROM (
SELECT [timestamp], CONVERT(xml, record) AS [record]
FROM sys.dm_os_ring_buffers
WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
AND record LIKE '%<SystemHealth>%') AS x
) AS y
ORDER BY record_id DESC
-- check if the average CPU utilization was over 80% in the past 10 minutes
SELECT @AvgCPUUtilization = AVG([SQLServerProcessCPUUtilization] + [OtherProcessCPU Utilization])
FROM #CPUUtilization
WHERE EventTime > DATEADD(MM, -10, GETDATE())
IF @AvgCPUUtilization >= 80
BEGIN
SELECT TOP(10)
CONVERT(VARCHAR(25),@AvgCPUUtilization) +'%' AS [AvgCPUUtilization]
, GETDATE() [Date and Time]
, r.cpu_time
, r.total_elapsed_time
, s.session_id
, s.login_name
, s.host_name
, DB_NAME(r.database_id) AS DatabaseName
, SUBSTRING (t.text,(r.statement_start_offset/2) + 1,
((CASE WHEN r.statement_end_offset = -1
THEN LEN(CONVERT(NVARCHAR(MAX), t.text)) * 2
ELSE r.statement_end_offset
END - r.statement_start_offset)/2) + 1) AS [IndividualQuery]
, SUBSTRING(text, 1, 200) AS [ParentQuery]
, r.status
, r.start_time
, r.wait_type
, s.program_name
INTO #PossibleCPUUtilizationQueries
FROM sys.dm_exec_sessions s
INNER JOIN sys.dm_exec_connections c ON s.session_id = c.session_id
INNER JOIN sys.dm_exec_requests r ON c.connection_id = r.connection_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE s.session_id > 50
AND r.session_id != @@spid
order by r.cpu_time desc
-- query the temp table, you can also send an email report to yourself or your development team
SELECT *
FROM #PossibleCPUUtilizationQueries
END
-- drop the temp tables
IF OBJECT_ID('TEMPDB..#CPUUtilization') IS NOT NULL
drop table #CPUUtilization
IF OBJECT_ID('TEMPDB..#PossibleCPUUtilizationQueries') IS NOT NULL
drop table #PossibleCPUUtilizationQueries