Month: October 2018
SQL Server: Complex Queries and Plan Timeout
I was speaking to a client recently about how very complex queries might not always be getting the most efficient query plan due to compilation timeouts. Joseph Pilov has written a good description here: Understanding Optimizer Timeout and how Complex queries can be Affected in SQL Server
SQL Agent Jobs Starting at the Same Time
The following query will list SQL Agent jobs that are due to start execution at the same time:
;with cteNextRunDatetime as ( SELECT job_id, next_scheduled_run_date = max(next_scheduled_run_date) FROM msdb.dbo.sysjobactivity WHERE next_scheduled_run_date IS NOT NULL GROUP BY job_id ) , cteSimultaneousJobsDates as ( SELECT next_scheduled_run_date FROM cteNextRunDatetime GROUP BY next_scheduled_run_date HAVING COUNT(*) > 1 ) SELECT sj.name, sj.description, c.next_scheduled_run_date FROM msdb.dbo.sysjobs sj JOIN cteNextRunDatetime sja ON sj.job_id = sja.job_id JOIN cteSimultaneousJobsDates c on c.next_scheduled_run_date = sja.next_scheduled_run_date WHERE enabled = 1 ORDER BY sja.next_scheduled_run_date
When Did My Azure SQL Database Server Restart?
Getting the server restart time for an on-premise SQL Server is simple, and in fact there are several ways using sys.dm_os_sys_info, sys.dm_exec_sessions, sys.traces, or sys.databases
In an Azure SQL Database, you don’t get access to those system objects.
Brent Ozar posted a way to get the approximate Azure SQL Database restart date/time but I found that some of the wait types can produce large outliers and skew the result:
Instead, I’ve modified to use a standard statistics technique to reject outlier values that are outside 1.5 times the interquartile range:
;with cte as ( SELECT wait_time_ms FROM sys.dm_os_wait_stats w with(nolock) WHERE wait_type IN ( 'BROKER_TASK_STOP', 'DIRTY_PAGE_POLL', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION', 'LAZYWRITER_SLEEP', 'LOGMGR_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_DISPATCHER_WAIT', 'XE_TIMER_EVENT' ) ) select approx_ms_since_restart = AVG(wait_time_ms), approximate_restart_date = DATEADD(s, AVG(-wait_time_ms)/1000, GETDATE()) from cte cross join (select q1 = min(wait_time_ms), q3 = max(wait_time_ms), iqr = max(wait_time_ms) - min(wait_time_ms) from (select wait_time_ms, row_number() over (order by wait_time_ms) as seqnum, count(*) over (partition by null) as total from cte ) t where seqnum = cast(total * 0.25 as int) or seqnum = cast(total * 0.75 as int) ) qts where (wait_time_ms >= q1 - 1.5 * iqr) AND (wait_time_ms <= q3 + 1.5 * iqr)
This tells me my Azure server restarted on the 21st Sept 2018 around 23:46.
It’s not as complicated as it first looks:
The first part is obviously the same as Brent Ozar’s query, it gets a list of waits and their respective cumulative wait times. We generate a row number ordered by wait time and the total number of rows, and then pick 2 values using the row number at positions a quarter and three quarters along that list (the first and third quartile values). We then reject any value that is smaller than the 1st quartile value minus 1.5 times the interquartile range and any value that is larger than the 3rd quartile value plus 1.5 times the interquartile range.
Note: DATEADD() only accepts an integer for its second parameter, and using milliseconds(ms) overflows for even short periods of server up-time. In fact, since it’s approximate it might be better to use:
approximate_restart_date = DATEADD(minute, AVG(-wait_time_ms)/60000, GETDATE())