You are on page 1of 11

SQL Script to Monitor CPU Utilization

This post helps you in understanding and using SQL Script to Monitor CPU utilization. There are certain
scenarios where we can quickly use this script to get SQL Server CPU utilization.

When there is a performance issue, and you need to quickly check the CPU usage

When doing Root Cause Analysis

When we only have access to SQL Server not to Windows

SQL Server CPU Utilization history report for last N minutes:

Below is the SQL Script to Monitor CPU utilization. This script captures the CPU usage history report
from last 10 min. This can be customized by changing the variable “@lastNmin” value.

SQL Server CPU Utilization history report for last N minutes

Transact-SQL

Press CTRL+C to Copy, CTRL+V to Paste

1 /***** Script: SQL Server CPU Utilization report from last N minutes *****/

2 /***** Support: SQL Server 2008 and Above *****/

3 /***** Tested On: SQL Server 2008 R2 and 2014 *****/

4 /***** Output:

5 SQLServer_CPU_Utilization: % CPU utilized from SQL Server Process

6 System_Idle_Process: % CPU Idle - Not serving to any process

7 Other_Process_CPU_Utilization: % CPU utilized by processes otherthan SQL Server

8 Event_Time: Time when these values captured

9 *****/

1
0
DECLARE @ts BIGINT;
1
DECLARE @lastNmin TINYINT;
1
SET @lastNmin = 10;
1 SELECT @ts =(SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info);
2
SELECT TOP(@lastNmin)
1
SQLProcessUtilization AS [SQLServer_CPU_Utilization],
3
SystemIdle AS [System_Idle_Process],
1
4 100 - SystemIdle - SQLProcessUtilization AS [Other_Process_CPU_Utilization],
1 DATEADD(ms,-1 *(@ts - [timestamp]),GETDATE())AS [Event_Time]
5
FROM (SELECT record.value('(./Record/@id)[1]','int')AS record_id,
1
6 record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]','int')A
S [SystemIdle],
1
7 record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]'
,'int')AS [SQLProcessUtilization],
1
8 [timestamp]

1 FROM (SELECT[timestamp], convert(xml, record) AS [record]


9 FROM sys.dm_os_ring_buffers
2 WHERE ring_buffer_type =N'RING_BUFFER_SCHEDULER_MONITOR'AND record
0 LIKE'%%')AS x )AS y
2 ORDER BY record_id DESC;
1

2
2

2
3

2
4

2
5

2
6

2
7

SQL Server Database wise CPU Utilization


From above script we come to know that SQL Server is utilizing high CPU, now the next step is to find
out which database from the SQL instance is causing the high CPU utilization. Below is the SQL Script to
Monitor CPU Utilization database wise.

SQL Server Database wise CPU Utilization

Transact-SQL

Press CTRL+C to Copy, CTRL+V to Paste

1 /***** Script: Database Wise CPU Utilization report *****/

2 /***** Support: SQL Server 2008 and Above *****/

3 /***** TestedOn: SQL Server 2008 R2 and 2014 *****/

4 /***** Output:

5 SNO: Serial Number

6 DBName: Databse Name

7 CPU_Time(Ms): CPU Time in Milliseconds

8 CPUPercent: Let’s say this instance is using 50% CPU and one of the database
is using 80%. It means the actual CPU usage from the database is calculated as: (80
9
/ 100) * 50 = 40 %
10
*****/
11
WITH DB_CPU AS
12
(SELECT DatabaseID,
13
DB_Name(DatabaseID)AS [DatabaseName],
14
SUM(total_worker_time)AS [CPU_Time(Ms)]
15
FROM sys.dm_exec_query_stats AS qs
16
CROSS APPLY(SELECT CONVERT(int, value)AS [DatabaseID]
17
FROM sys.dm_exec_plan_attributes(qs.plan_handle)
18
WHERE attribute =N'dbid')AS epa GROUP BY DatabaseID)
19
SELECT ROW_NUMBER()OVER(ORDER BY [CPU_Time(Ms)] DESC)AS [SNO],
20
21 DatabaseName AS [DBName], [CPU_Time(Ms)],

22 CAST([CPU_Time(Ms)] * 1.0 /SUM([CPU_Time(Ms)]) OVER()* 100.0 AS DECIMAL(5,


2))AS [CPUPercent]
23
FROM DB_CPU
24
WHERE DatabaseID > 4 -- system databases

AND DatabaseID <> 32767 -- ResourceDB

ORDER BY SNO OPTION(RECOMPILE);

SQL Server Query Wise CPU Utilization

From the above scripts you confirmed that there is a huge CPU utilization from one of the SQL Instance
and you identified the database which is causing high CPU. Now the next step is to identify top 10
queries which are causing high CPU utilization. Here is the SQL Script to Monitor CPU Utilization query
level. This is a wonderful script provided by SQLBlog and SQLKnowlwdge.

Note: Remember that it returns the list of costly queries which causes high CPU utilization when only
the CPU usage is >=80% from last 10 Min, otherwise it returns nothing. You can modify the script as per
your needs.

SQL Server Query Wise CPU Utilization

Transact-SQL

Press CTRL+C to Copy, CTRL+V to Paste

1 /***** Script: Top 10 queries that causes high CPU Utilization *****/

2 /***** Support: SQL Server 2008 and Above *****/

3 /***** TestedOn: SQL Server 2008,R2 and 2014 *****/

4 /***** Output: All query related details *****/

5 /***** Note: This script returns list of costly queries when CPU utilization is >=80%
from last 10 min ****/
6

7
SET NOCOUNT ON
8
DECLARE @ts_now bigint
9
1 DECLARE @AvgCPUUtilization DECIMAL(10,2)
0

1
SELECT @ts_now = cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info
1

1
2 -- load the CPU utilization in the past 10 minutes into the temp table, you can load
them into a permanent table
1
3 SELECT TOP(10) SQLProcessUtilization AS [SQLServerProcessCPUUtilization]
1 ,SystemIdle AS [SystemIdleProcess]
4
,100 - SystemIdle - SQLProcessUtilization AS [OtherProcessCPU Utilization]
1
5 ,DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [EventTime]

1 INTO #CPUUtilization
6 FROM (
1 SELECT record.value('(./Record/@id)[1]', 'int') AS record_id,
7
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]',
1 'int')
8
AS [SystemIdle],
1
9 record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilizati
on)[1]',
2
0 'int')

2 AS [SQLProcessUtilization], [timestamp]
1 FROM (
2 SELECT [timestamp], CONVERT(xml, record) AS [record]
2
FROM sys.dm_os_ring_buffers
2
3 WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'

2 AND record LIKE '%<SystemHealth>%') AS x


4
) AS y
2
ORDER BY record_id DESC
5

2
6
2 -- check if the average CPU utilization was over 80% in the past 10 minutes
7
SELECT @AvgCPUUtilization = AVG([SQLServerProcessCPUUtilization] +
2 [OtherProcessCPU Utilization])
8
FROM #CPUUtilization
2
WHERE EventTime > DATEADD(MM, -10, GETDATE())
9

3
0 IF @AvgCPUUtilization >= 80
3 BEGIN
1
SELECT TOP(10)
3
2 CONVERT(VARCHAR(25),@AvgCPUUtilization) +'%' AS [AvgCPUUtilization]

3 , GETDATE() [Date and Time]


3 , r.cpu_time
3 , r.total_elapsed_time
4
, s.session_id
3
5 , s.login_name

3 , s.host_name
6 , DB_NAME(r.database_id) AS DatabaseName
3 , SUBSTRING (t.text,(r.statement_start_offset/2) + 1,
7
((CASE WHEN r.statement_end_offset = -1
3
8 THEN LEN(CONVERT(NVARCHAR(MAX), t.text)) * 2

3 ELSE r.statement_end_offset
9
END - r.statement_start_offset)/2) + 1) AS [IndividualQuery]
4
, SUBSTRING(text, 1, 200) AS [ParentQuery]
0
, r.status
4
1 , r.start_time

4 , r.wait_type
2
, s.program_name
4
INTO #PossibleCPUUtilizationQueries
3
4 FROM sys.dm_exec_sessions s
4
INNER JOIN sys.dm_exec_connections c ON s.session_id = c.session_id
4
INNER JOIN sys.dm_exec_requests r ON c.connection_id = r.connection_id
5
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
4
6 WHERE s.session_id > 50
4 AND r.session_id != @@spid
7
order by r.cpu_time desc
4
8 -- query the temp table, you can also send an email report to yourself or your
development team
4
9 SELECT *

5 FROM #PossibleCPUUtilizationQueries
0 END
5
1
-- drop the temp tables
5
2 IF OBJECT_ID('TEMPDB..#CPUUtilization') IS NOT NULL

5 drop table #CPUUtilization


3

5 IF OBJECT_ID('TEMPDB..#PossibleCPUUtilizationQueries') IS NOT NULL


4
drop table #PossibleCPUUtilizationQueries
5
5

5
6

5
7

5
8

5
9

6
0
6
1

6
2

6
3

6
4

6
5

6
6

6
7

6
8

6
9

7
0

7
1

7
2

7
3

7
4

7
5

7
6

7
7
7
8

7
9

8
0

Script to find Top 20 Costliest Stored Procedures – High CPU:

This script sourced from here. It results a list of stored procedures which are utilizing high CPU. This
script goes through the buffer cache and find out these results based on Total and Average worker
thread times. Below is the SQL Script to Monitor CPU Utilization from the stored procedure point of
view.

Script to find Top 20 Costliest Stored Procedures – High CPU

Transact-SQL

Press CTRL+C to Copy, CTRL+V to Paste

1 /***** Script: Top 20 Stored Procedures using High CPU *****/

2 /***** Support: SQL Server 2008 and Above *****/

3 /***** Tested On: SQL Server 2008 R2 and 2014 *****/

4 /***** Output:

5 SP Name: Stored Procedure Name

6 TotalWorkerTime: Total Worker Time since the last compile time

7 AvgWorkerTime: Average Worker Time since last compile time

8 execution_count: Total number of execution since last compile time

9 Calls/Second: Number of calls / executions per second

10 total_elapsed_time: total elapsed time

11 avg_elapsed_time: Average elapsed time

12 cached_time: Procedure Cached time

13 *****/
14 SELECT TOP (20)

15 p.name AS [SP Name],

16 qs.total_worker_time AS [TotalWorkerTime],

17 qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],

18 qs.execution_count,

19 ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS


[Calls/Second],
20
qs.total_elapsed_time,
21
qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
22
qs.cached_time
23
FROM sys.procedures AS p WITH (NOLOCK)
24
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK) ON p.[object_id] =
25
qs.[object_id]
26
WHERE qs.database_id = DB_ID()

ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);

Script to find Top 20 Costliest Queries – High CPU

This script sourced from here. It results a list of queries which are utilizing high CPU. Below is the SQL
Script to Monitor CPU Utilization from Ad-hoc queries.

Script to find Top 20 Costliest Queries – High CPU

Transact-SQL

Press CTRL+C to Copy, CTRL+V to Paste

1 /***** Script: Top 20 Stored Procedures using High CPU *****/

2 /***** Support: SQL Server 2008 and Above *****/

3 /***** Tested On: SQL Server 2008 R2 and 2014 *****/

4 /***** Output: Queries, CPU, Elapsed Times, Ms and S ****/


5 SELECT TOP (20)

6 st.text AS Query,

7 qs.execution_count,

8 qs.total_worker_time AS Total_CPU,

9 total_CPU_inSeconds = --Converted from microseconds

10 qs.total_worker_time/1000000,

11 average_CPU_inSeconds = --Converted from microseconds

12 (qs.total_worker_time/1000000) / qs.execution_count,

13 qs.total_elapsed_time,

14 total_elapsed_time_inSeconds = --Converted from microseconds

15 qs.total_elapsed_time/1000000,

16 qp.query_plan

17 FROM sys.dm_exec_query_stats AS qs

18 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st

19 CROSS apply sys.dm_exec_query_plan (qs.plan_handle) AS qp

20 ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);

Summary:

I believe these scripts will be helpful to quickly get the Instance, Database and Query level CPU
utilization reports.

You might also like