
sp_whoisactive 11.32

SQL Server, performance, data, analytics

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
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
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())