sql server显示缺少日期

时间:2014-03-11 15:10:22

标签: sql sql-server

SiteVisitID     siteName        visitDate
------------------------------------------------------    
   1            site1           01/03/2014
   2            Site2           01/03/2014
   3            site1           02/03/2014
   4            site1           03/03/2014
   5            site2           03/03/2014
   6            site1           04/03/2014
   7            site2           04/03/2014
   8            site2           05/03/2014
   9            site1           06/03/2014
  10            site2           06/03/2014
  11            site1           08/03/2014
  12            site2           08/03/2014
  13            site1           09/03/2014
  14            site2           10/03/2014

有两个网站,每个网站都需要每月的访问条目,所以考虑到今天是2014年3月11日我们预计有22个参赛作品,但只有14个条目如此缺失8,有什么办法吗?在sql中我们可以提取缺少的日期条目

截至当月对网站的当天

siteName    missingDate
-----------------------    
site2       02/03/2014
site1       05/03/2014
site1       07/03/2014
site2       07/03/2014
site2       09/03/2014
site1       10/03/2014
site1       11/03/2014
site2       11/03/2014

这是我在逻辑和语法上错误的尝试

select 
    siteName, visitDate  
from  
    SiteVisit not in (SELECT siteName, visitDate
                      FROM SiteVisit 
                      WHERE Day(visitDate) != Day(CURRENT_TIMESTAMP) 
                        AND Month(visitDate) = Month(CURRENT_TIMESTAMP))

注意:上面的数据和列是实际表的简化版

4 个答案:

答案 0 :(得分:5)

我建议您使用table valued function在两个选定日期之间的所有日子作为表格(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

(我包括一个简单的表格设置,便于复制品测试)

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'

现在,您可以简单地查看当您知道&#34;边界日期时没有访问的日期&#34;比如:

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

或者,如果您想知道未访问任何网站的所有日子,您可以使用此查询:

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

请注意:您的表中似乎有一些重复(未规范化)siteName应该真正进入一个单独的表格,并且只能从SiteVisit引用

答案 1 :(得分:2)

如果你有一个&#39;日历表,这会变得更加简单。也就是说,这个表格包含您可能感兴趣的每个日期。

原因是SQL无法报告不存在的内容。您可以编写SQL以创建应该属于间隙的日期(例如此处的另一个答案),然后报告创建的日期。或者你可以将它们放在一个持久的日历表中。并且只是使用它,使SQL更简单,更频繁,然后更快,更快。

在类似的徒劳中,我假设您有一个维度表,其中包含可以访问的所有站点?如果是这样,那么你得到这样的东西......

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

答案 2 :(得分:2)

也许您可以将此作为起点:

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

答案 3 :(得分:0)

在这里,我正在实现用户定义的功能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

使用该函数和数据集,我们将进行cross apply来生成缺少的日期集。

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