--=========================================--
/*** SQL SERVER CPU MONITORING QUERIES ***/
--=========================================------
-- Supported Versions SQL server 2008 and higher
-------------------------------------------------

-------------------------------------
/*** 1. Current CPU Utilization ***/
-------------------------------------

DECLARE @ts BIGINT; 
DECLARE @lastNmin TINYINT; 

SELECT @ts = (SELECT cpu_ticks / ( cpu_ticks / ms_ticks ) 
              FROM   sys.dm_os_sys_info); 

SELECT TOP(1) Dateadd(ms, -1 * ( @ts - [timestamp] ), Getdate())AS [EventTime], 
              sqlprocessutilization                             AS 
              [SQL Server Utilization], 
              100 - systemidle - sqlprocessutilization          AS 
              [Other Process CPU_Utilization], 
              systemidle                                        AS [System Idle] 
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'%%')AS x)AS y 
ORDER  BY record_id DESC; 


---------------------------------------------
/*** 2.CPU Utilization for last N minutes***/
---------------------------------------------
-- Mention the minutes in @lastNmin Parameter

DECLARE @ts BIGINT; 
DECLARE @lastNmin TINYINT; 

SET @lastNmin = 15; --Mention the Minutes Here 

SELECT @ts = (SELECT cpu_ticks / ( cpu_ticks / ms_ticks ) 
              FROM   sys.dm_os_sys_info); 

SELECT TOP(@lastNmin) Dateadd(ms, -1 * ( @ts - [timestamp] ), Getdate())AS 
                      [EventTime], 
                      sqlprocessutilization                             AS 
                      [SQL Server Utilization], 
                      100 - systemidle - sqlprocessutilization          AS 
                      [Other Process CPU_Utilization], 
                      systemidle                                        AS 
                      [System Idle] 
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'%%')AS x)AS y 
ORDER  BY record_id DESC; 


------------------------------------------
/*** 3.Database wise CPU Utilization ***/
------------------------------------------

WITH db_cpu 
     AS (SELECT databaseid, 
                Db_name(databaseid)   AS [DatabaseName], 
                Sum(total_worker_time)AS [CPU_Time(Ms)] 
         FROM   sys.dm_exec_query_stats AS qs 
                CROSS apply(SELECT CONVERT(INT, value)AS [DatabaseID] 
                            FROM   sys.Dm_exec_plan_attributes(qs.plan_handle) 
                            WHERE  attribute = N'dbid')AS epa 
         GROUP  BY databaseid) 
SELECT Row_number() 
         OVER( 
           ORDER BY [cpu_time(ms)] DESC)                             AS [SNO], 
       databasename                                                  AS [DBName] 
       , 
       [cpu_time(ms)], 
       Cast([cpu_time(ms)] * 1.0 / Sum([cpu_time(ms)]) 
                                     OVER() * 100.0 AS DECIMAL(5, 2))AS 
       [CPUPercent] 
FROM   db_cpu 
WHERE  databaseid > 4 -- system databases  
       AND databaseid <> 32767 -- ResourceDB  
ORDER  BY sno 
OPTION(recompile); 


---------------------------------------
/*** 4.Query Wise CPU Utilization ***/
---------------------------------------
-- This Query will show the queries and its CPU time if the avg CPU usgae is > 50
-- You can modify this in IF @AvgCPUUtilization >= 50

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 50% in the past 10 minutes 

SELECT @AvgCPUUtilization = Avg([sqlserverprocesscpuutilization] 
                                + [otherprocesscpu utilization]) 
FROM   #cpuutilization 
WHERE  eventtime > Dateadd(mm, -10, Getdate()) 

IF @AvgCPUUtilization >= 50 
  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 

      
      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 


---------------------------------
/*** 5.TOP costliest Queries ***/
---------------------------------
-- This will give top 20 costliest queries which are executed recently. 

SELECT TOP (20) st.text              AS Query, 
                qs.execution_count, 
                qs.total_worker_time AS Total_CPU, 
                total_CPU_inSeconds = --Converted from microseconds 
                qs.total_worker_time / 1000000, 
                average_CPU_inSeconds = --Converted from microseconds 
                ( qs.total_worker_time / 1000000 ) / qs.execution_count, 
                qs.total_elapsed_time, 
                total_elapsed_time_inSeconds = --Converted from microseconds 
                qs.total_elapsed_time / 1000000, 
                qp.query_plan 
FROM   sys.dm_exec_query_stats AS qs 
       CROSS apply sys.Dm_exec_sql_text(qs.sql_handle) AS st 
       CROSS apply sys.Dm_exec_query_plan (qs.plan_handle) AS qp 
ORDER  BY qs.total_worker_time DESC 
OPTION (recompile); 


-------------------------------------------------------------
/*** 5.TOP costliest Queries with batch and more details ***/
-------------------------------------------------------------

SELECT TOP 50
  [Avg. MultiCore/CPU time(sec)] = qs.total_worker_time / 1000000 / qs.execution_count,
  [Total MultiCore/CPU time(sec)] = qs.total_worker_time / 1000000,
  [Avg. Elapsed Time(sec)] = qs.total_elapsed_time / 1000000 / qs.execution_count,
  [Total Elapsed Time(sec)] = qs.total_elapsed_time / 1000000,
  qs.execution_count,
  [Avg. I/O] = (total_logical_reads + total_logical_writes) / qs.execution_count,
  [Total I/O] = total_logical_reads + total_logical_writes,
  Query = SUBSTRING(qt.[text], (qs.statement_start_offset / 2) + 1,
  (
  (
  CASE qs.statement_end_offset
    WHEN -1 THEN DATALENGTH(qt.[text])
    ELSE qs.statement_end_offset
  END - qs.statement_start_offset
  ) / 2
  ) + 1
  ),
  Batch = qt.[text],
  [DB] = DB_NAME(qt.[dbid]),
  qs.last_execution_time,
  qp.query_plan
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp

ORDER BY [Total MultiCore/CPU time(sec)] DESC;