sql server displaying missing dates

前端 未结 4 1651
盖世英雄少女心
盖世英雄少女心 2020-12-04 01:51
SiteVisitID     siteName        visitDate
------------------------------------------------------    
   1            site1           01/03/2014
   2            Site2         


        
相关标签:
4条回答
  • 2020-12-04 02:28

    I would recommend you use a table valued function to get you all days in between 2 selected dates as a table (Try it out in this fiddle):

    CREATE FUNCTION dbo.GetAllDaysInBetween(@FirstDay DATETIME, @LastDay DATETIME)
    RETURNS @retDays TABLE 
    (
        DayInBetween DATETIME
    )
    AS 
    BEGIN
        DECLARE @currentDay DATETIME
        SELECT @currentDay = @FirstDay
    
        WHILE @currentDay <= @LastDay
        BEGIN
    
            INSERT @retDays (DayInBetween)
                SELECT @currentDay
    
            SELECT @currentDay = DATEADD(DAY, 1, @currentDay)
        END 
    
        RETURN
    END
    

    (I include a simple table setup for easy copypaste-tests)

    CREATE TABLE SiteVisit (ID INT PRIMARY KEY IDENTITY(1,1), visitDate DATETIME, visitSite NVARCHAR(512))
    
    INSERT INTO SiteVisit (visitDate, visitSite)
        SELECT '2014-03-11', 'site1'
        UNION
        SELECT '2014-03-12', 'site1'
        UNION
        SELECT '2014-03-15', 'site1'
        UNION
        SELECT '2014-03-18', 'site1'
        UNION
        SELECT '2014-03-18', 'site2'
    

    now you can simply check what days no visit occured when you know the "boundary days" such as this:

    SELECT
            DayInBetween AS missingDate,
            'site1' AS visitSite
        FROM dbo.GetAllDaysInBetween('2014-03-11', '2014-03-18') AS AllDaysInBetween
        WHERE NOT EXISTS 
            (SELECT ID FROM SiteVisit WHERE visitDate = AllDaysInBetween.DayInBetween AND visitSite = 'site1')
    

    Or if you like to know all days where any site was not visited you could use this query:

    SELECT
            DayInBetween AS missingDate,
            Sites.visitSite
        FROM dbo.GetAllDaysInBetween('2014-03-11', '2014-03-18') AS AllDaysInBetween
        CROSS JOIN (SELECT DISTINCT visitSite FROM SiteVisit) AS Sites
        WHERE NOT EXISTS
            (SELECT ID FROM SiteVisit WHERE visitDate = AllDaysInBetween.DayInBetween AND visitSite = Sites.visitSite)
        ORDER BY visitSite
    

    Just on a side note: it seems you have some duplication in your table (not normalized) siteName should really go into a separate table and only be referenced from SiteVisit

    0 讨论(0)
  • 2020-12-04 02:31

    Maybe you can use this as a starting point:

    -- Recursive CTE to generate a list of dates for the month
    -- You'll probably want to play with making this dynamic
    WITH Dates AS
    (
        SELECT 
            CAST('2014-03-01' AS datetime) visitDate
        UNION ALL
        SELECT 
            visitDate + 1
        FROM 
            Dates
        WHERE
            visitDate + 1 < '2014-04-01'
    )
    -- Build a list of siteNames with each possible date in the month
    , SiteDates AS 
    (
        SELECT 
            s.siteName, d.visitDate 
        FROM 
            SiteVisitEntry s
        CROSS APPLY 
            Dates d
        GROUP BY 
            s.siteName, d.visitDate
    )
    -- Use a left anti-semi join to find the missing dates
    SELECT
        d.siteName, d.visitDate AS missingDate
    FROM
        SiteDates d
    LEFT JOIN
        SiteVisitEntry e /* Plug your actual table name here */
        ON
        d.visitDate = e.visitDate
        AND
        d.siteName = e.siteName
    WHERE
        e.visitDate IS NULL
    OPTION 
        (MAXRECURSION 0)
    
    0 讨论(0)
  • 2020-12-04 02:39

    Here I am implementing a user-defined function genrate.

    CREATE FUNCTION genrate(@FROM_date date,@to_date date) 
    RETURNS @tab table(missing date) 
    AS 
    BEGIN 
        ;WITH cte AS
          ( 
            SELECT @FROM_date missing
            UNION ALL 
            SELECT DATEADD(DAY,1,missing)
            FROM cte
            WHERE missing <@to_date 
          )
    
        INSERT INTO @tab
        SELECT *
        FROM cte
        WHERE missing NOT IN (@FROM_date, @to_date) 
        RETURN 
    END
    

    Using the function with the data set we are going to do the cross apply to generate the missing along with date set.

    SELECT user_No,
         product_name,
         amount,
         b.missing AS Missing_date
    FROM date_tab t 
    CROSS APPLY DBO.genrate(T.ORDER_DATE,T.DELIVERY) b
    
    0 讨论(0)
  • 2020-12-04 02:45

    This becomes much simpler if you have a 'calendar table'. That is, a table that holds every date of possible interest to you.

    The reason for this is that SQL can't report on what's not there. You can write SQL to create the dates that should belong in the gaps (such as in another answer here), then report on the dates created. Or you could just have them in a persisted 'calendar table' and just use that, making the SQL simpler and more often then not, significantly quicker.

    In a similar vain, I assume that you have a dimension table that contains all of the sites which can have visits? If so, then you get something like this...

    SELECT
      *
    FROM
      site
    CROSS JOIN
      calendar
    LEFT JOIN
      visit
        ON  visit.site_id   = site.id
        AND visit.visitDate = calendar.date
    WHERE
          calendar.date   >= DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) 
      AND calendar.date   <                                    GETDATE()
      AND visit.visitDate IS NULL
    
    0 讨论(0)
提交回复
热议问题