IF NOT EXISTS (
	SELECT *
	FROM sys.tables
	WHERE object_id = object_id('dbo.WaitStats')
)
BEGIN
	CREATE TABLE dbo.WaitStats
	(
		WaitType NVARCHAR(60),
		WaitingTasksCount BIGINT,
		WaitTimeMs BIGINT,
		MaxWaitTimeMs BIGINT,
		SignalWaitTimeMs BIGINT,
		CollectionDate DATETIME,
		CONSTRAINT PK_WaitStats
			PRIMARY KEY CLUSTERED (WaitType, CollectionDate)
	);
END
GO
CREATE OR ALTER VIEW dbo.ImportantWaits
AS
	SELECT * 
	FROM dbo.WaitStats
	WHERE WaitType NOT IN (
        N'BROKER_EVENTHANDLER', 
        N'BROKER_RECEIVE_WAITFOR', 
        N'BROKER_TASK_STOP', 
        N'BROKER_TO_FLUSH', 
        N'BROKER_TRANSMITTER', 
        N'CHECKPOINT_QUEUE', 
        N'CHKPT', 
        N'CLR_AUTO_EVENT', 
        N'CLR_MANUAL_EVENT', 
        N'CLR_SEMAPHORE', 
        N'CXCONSUMER', 
        N'DBMIRROR_DBM_EVENT', 
        N'DBMIRROR_EVENTS_QUEUE', 
        N'DBMIRROR_WORKER_QUEUE', 
        N'DBMIRRORING_CMD', 
        N'DIRTY_PAGE_POLL', 
        N'DISPATCHER_QUEUE_SEMAPHORE', 
        N'EXECSYNC', 
        N'FSAGENT', 
        N'FT_IFTS_SCHEDULER_IDLE_WAIT', 
        N'FT_IFTSHC_MUTEX', 
        N'HADR_CLUSAPI_CALL', 
        N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', 
        N'HADR_LOGCAPTURE_WAIT', 
        N'HADR_NOTIFICATION_DEQUEUE', 
        N'HADR_TIMER_TASK', 
        N'HADR_WORK_QUEUE', 
        N'KSOURCE_WAKEUP', 
        N'LAZYWRITER_SLEEP', 
        N'LOGMGR_QUEUE', 
        N'MEMORY_ALLOCATION_EXT', 
        N'ONDEMAND_TASK_QUEUE', 
        N'PARALLEL_REDO_DRAIN_WORKER', 
        N'PARALLEL_REDO_LOG_CACHE', 
        N'PARALLEL_REDO_TRAN_LIST', 
        N'PARALLEL_REDO_WORKER_SYNC', 
        N'PARALLEL_REDO_WORKER_WAIT_WORK', 
        N'PREEMPTIVE_OS_FLUSHFILEBUFFERS', 
        N'PREEMPTIVE_XE_GETTARGETSTATE', 
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', 
        N'PWAIT_DIRECTLOGCONSUMER_GETNEXT', 
        N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', 
        N'QDS_ASYNC_QUEUE', 
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
        N'QDS_SHUTDOWN_QUEUE', 
        N'REDO_THREAD_PENDING_WORK', 
        N'REQUEST_FOR_DEADLOCK_SEARCH', 
        N'RESOURCE_QUEUE', 
        N'SERVER_IDLE_CHECK', 
        N'SLEEP_BPOOL_FLUSH', 
        N'SLEEP_DBSTARTUP', 
        N'SLEEP_DCOMSTARTUP', 
        N'SLEEP_MASTERDBREADY', 
        N'SLEEP_MASTERMDREADY', 
        N'SLEEP_MASTERUPGRADED', 
        N'SLEEP_MSDBSTARTUP', 
        N'SLEEP_SYSTEMTASK', 
        N'SLEEP_TASK', 
        N'SLEEP_TEMPDBSTARTUP', 
        N'SNI_HTTP_ACCEPT', 
        N'SOS_WORK_DISPATCHER', 
        N'SP_SERVER_DIAGNOSTICS_SLEEP', 
        N'SQLTRACE_BUFFER_FLUSH', 
        N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', 
        N'SQLTRACE_WAIT_ENTRIES', 
        N'VDI_CLIENT_OTHER', 
        N'WAIT_FOR_RESULTS', 
        N'WAITFOR', 
        N'WAITFOR_TASKSHUTDOWN', 
        N'WAIT_XTP_RECOVERY', 
        N'WAIT_XTP_HOST_WAIT', 
        N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 
        N'WAIT_XTP_CKPT_CLOSE', 
        N'XE_DISPATCHER_JOIN', 
        N'XE_DISPATCHER_WAIT', 
        N'XE_TIMER_EVENT');
GO
CREATE OR ALTER PROCEDURE dbo.s_CollectWaitStats (
	@WaitThresholdInMs BIGINT
)
AS
	INSERT dbo.WaitStats( 
		WaitType, 
		WaitingTasksCount, 
		WaitTimeMs, 
		MaxWaitTimeMs,
		SignalWaitTimeMs,
		CollectionDate
	)
	SELECT 
		wait_type, 
		waiting_tasks_count, 
		wait_time_ms, 
		max_wait_time_ms,
		signal_wait_time_ms,
		GETUTCDATE()
	FROM sys.dm_os_wait_stats
	WHERE wait_time_ms > @WaitThresholdInMs;

	-- Clean up
	DELETE dbo.WaitStats
	WHERE CollectionDate < DATEADD( DAY, -7, GETUTCDATE() );
GO
CREATE OR ALTER PROCEDURE dbo.s_WaitStatsHistogram
	@HistogramBucketSizeInMinutes INT = 60
AS

WITH WaitStatsHistogram AS
(
    SELECT W.WaitType,
           C.GroupedCollectionDate AS CollectionDate,
           MAX(WaitTimeMs) AS WaitTimeMs
    FROM   ImportantWaits W
    CROSS APPLY ( SELECT DATEADD(
                    MINUTE, 
                    (DATEDIFF(MINUTE, 0, W.CollectionDate ) / @HistogramBucketSizeInMinutes) * @HistogramBucketSizeInMinutes, 
                    0) ) AS C(GroupedCollectionDate)
    GROUP BY W.WaitType, C.GroupedCollectionDate
)
SELECT WaitType,
       CollectionDate,
       WaitTimeMs - 
  	     LAG(WaitTimeMs, 1, NULL) OVER ( 
           PARTITION BY (WaitType) 
           ORDER BY (CollectionDate)) as WaitTimeMs
FROM   WaitStatsHistogram;
GO
DECLARE @DBName sysname = DB_NAME();
DECLARE @JobName sysname = 'CollectWaitStats';
DECLARE @JobDesc sysname
    = ' Source:   ' + 'https://github.com/mjswart/CollectWaitStats. ;' 
	+ ' Created:   '+ CAST(GETDATE() AS VARCHAR(20)) + ';'
    + ' By:    '+ SUSER_NAME() + ';';

IF NOT EXISTS 
(
	SELECT *
FROM msdb.dbo.sysjobs
WHERE name = @JobName
)
BEGIN
	EXEC msdb.dbo.sp_add_job 
		@job_name = @JobName,
		@description = @JobDesc;

	EXEC msdb.dbo.sp_add_jobstep 
		@job_name = @JobName,
		@step_name = N'Collect Wait Stats',
		@command = N'exec s_CollectWaitStats @WaitThresholdInMs = 1000;', 
		@database_name = @DBName;

	EXEC msdb.dbo.sp_add_jobserver 
		@job_name = @JobName,
		@server_name = N'(local)';

	EXEC msdb.dbo.sp_add_jobschedule 
		@job_name = @JobName,
		@name=N'Minutely', 
		@enabled=1, 
		@freq_type=4, 
		@freq_interval=1, 
		@freq_subday_type=4, 
		@freq_subday_interval=1;
END
GO
