• Just so folks are aware, pietlinden's solution will work, but may have incorrect results if that script fails to run on a given day.  If that fact is detected and corrected, that's fine, but if it's not, you could potentially identify a failure group that shouldn't actually be one because let's assume that the day the script failed is also a day no failures occur, but the 2 days prior had a failure, and the day after also did (there is at least one other similar scenario).   The script he provided will NOT detect that missing date, but because the 3 days of data that it has (in the sample data) would all be failures and would be in sequence, it would identify a failure, when had the script run on all days, it would not have identified it.

    Here's a query that will identify missing data:

    SET NOCOUNT ON;

    CREATE table #temp (
        Srvname varchar(100),
        [status] varchar(100),
        run_dt datetime
    );

    INSERT INTO #temp (Srvname, [status], run_dt)
    SELECT 'Srv1','Success','2017-02-20 11:50:20.010'
    UNION ALL
    SELECT 'Srv1','Success','2017-02-19 11:50:37.490'
    UNION ALL
    SELECT 'Srv1','Success','2017-02-18 11:50:47.400'
    UNION ALL
    SELECT 'Srv1','Success','2017-02-17 11:50:20.010'
    UNION ALL
    SELECT 'Srv2','Failed','2017-02-20 11:50:20.010'
    UNION ALL
    SELECT 'Srv2','Success','2017-02-19 11:50:37.490'
    UNION ALL
    SELECT 'Srv2','Failed','2017-02-18 11:50:47.400'
    UNION ALL
    SELECT 'Srv2','Success','2017-02-17 11:50:20.010'
    UNION ALL
    SELECT 'Srv3','Success','2017-02-20 11:50:20.010'
    UNION ALL
    SELECT 'Srv3','Success','2017-02-19 11:50:20.010'
    UNION ALL
    SELECT 'Srv3','Success','2017-02-18 11:50:20.010'
    UNION ALL
    SELECT 'Srv3','Success','2017-02-17 11:50:20.010'
    UNION ALL
    SELECT 'Srv4','Failed','2017-02-20 11:50:20.010'
    UNION ALL
    SELECT 'Srv4','Failed','2017-02-19 11:50:20.010'
    UNION ALL
    SELECT 'Srv4','Failed','2017-02-18 11:50:20.010'
    UNION ALL
    SELECT 'Srv4','Failed','2017-02-17 11:50:20.010'
    UNION ALL
    SELECT 'Srv5','Failed','2017-02-20 11:50:20.010'
    UNION ALL
    SELECT 'Srv5','Failed','2017-02-19 11:50:20.010'
    UNION ALL
    SELECT 'Srv5','Failed','2017-02-18 11:50:20.010'
    UNION ALL
    SELECT 'Srv5','Failed','2017-02-17 11:50:20.010'
    UNION ALL
    SELECT 'Srv6','Failed','2017-02-27 11:50:20.010'
    UNION ALL
    SELECT 'Srv6','Failed','2017-02-26 11:50:20.010'
    UNION ALL
    SELECT 'Srv6','Failed','2017-02-24 11:50:20.010'
    UNION ALL
    SELECT 'Srv6','Failed','2017-02-23 11:50:20.010';

    /*
    SELECT *
    FROM #temp
    ORDER BY Srvname, run_dt;
    */

    DECLARE @MIN_DATE AS date;
    DECLARE @MAX_DATE AS date;

    SELECT @MIN_DATE = MIN(run_dt), @MAX_DATE = MAX(run_dt)
    FROM #temp;

    WITH NUMBERS AS (

        SELECT 1 AS N UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
    ),
        ALL_DATES AS (

            SELECT TOP (DATEDIFF(day, @MIN_DATE, @MAX_DATE) + 1)
                DATEADD(day, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1, @MIN_DATE) AS THE_DATE
            FROM NUMBERS AS N1, NUMBERS AS N2, NUMBERS AS N3, NUMBERS AS N4
    ),
        ALL_SERVERS AS (

            SELECT DISTINCT Srvname
            FROM #temp
    ),
        ALL_SERVERS_ALL_DATES AS (

            SELECT S.Srvname, D.THE_DATE
            FROM ALL_SERVERS AS S
                CROSS APPLY ALL_DATES AS D
    ),
        FAILURE_GROUPS AS (

            SELECT T.Srvname, D.THE_DATE, T.[status],
                DATEDIFF(day, LAG(D.THE_DATE, 2) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE), D.THE_DATE) AS LAG2_DIFF,
                DATEDIFF(day, LAG(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE), D.THE_DATE) AS LAG1_DIFF,
                DATEDIFF(day, D.THE_DATE, LEAD(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE)) AS LEAD1_DIFF,
                DATEDIFF(day, D.THE_DATE, LEAD(D.THE_DATE, 2) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE)) AS LEAD2_DIFF,
                CASE
                    WHEN DATEDIFF(day, D.THE_DATE, LEAD(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE)) = 1
                        AND DATEDIFF(day, D.THE_DATE, LEAD(D.THE_DATE, 2) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE)) = 2 THEN 1
                    WHEN DATEDIFF(day, LAG(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE), D.THE_DATE) = 1
                        AND DATEDIFF(day, D.THE_DATE, LEAD(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE)) = 1 THEN 1
                    WHEN DATEDIFF(day, LAG(D.THE_DATE, 2) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE), D.THE_DATE) = 2
                        AND DATEDIFF(day, LAG(D.THE_DATE, 1) OVER(PARTITION BY T.Srvname ORDER BY D.THE_DATE), D.THE_DATE) = 1 THEN 1
                    ELSE 0
                END AS IS_FAILURE_GROUP
            FROM #temp AS T
                INNER JOIN ALL_DATES AS D
                    ON CAST(T.run_dt AS date) = D.THE_DATE
            WHERE T.[status] = 'Failed'
    ),
        MISSING_DATA AS (

            SELECT DISTINCT SD.Srvname + ' - MISSING DATA' AS Srvname, CAST(NULL AS int) AS FailureCount,
                SD.THE_DATE AS MinFailDate, SD.THE_DATE AS MaxFailDate
            FROM ALL_SERVERS_ALL_DATES AS SD
                LEFT OUTER JOIN #temp AS T
                    ON SD.Srvname = T.Srvname
                    AND SD.THE_DATE = CAST(T.run_dt AS date)
            WHERE T.Srvname IS NULL
    )
    SELECT G.Srvname, COUNT(*) AS FailureCount, MIN(G.THE_DATE) AS MinFailDate, MAX(G.THE_DATE) AS MaxFailDate
    FROM FAILURE_GROUPS AS G
    WHERE G.IS_FAILURE_GROUP = 1
    GROUP BY G.Srvname
    UNION ALL
    SELECT Srvname, FailureCount, MinFailDate, MaxFailDate
    FROM MISSING_DATA
    ORDER BY Srvname;

    DROP TABLE #temp;

    Steve (aka sgmunson) 🙂 🙂 🙂
    Rent Servers for Income (picks and shovels strategy)