使用DatePart(),其日期范围跨越datepart边界

时间:2013-05-19 04:46:22

标签: ms-access

我目前正在尝试将一些数据表汇总到报告中。表中的每条记录都包含一个日期范围,如下所示:

StartDate    EndDate
--------------------
13/04/13     15/04/13
17/04/13     24/04/13
28/04/13     03/05/13
05/05/13     10/05/13

假设日期范围表示休假天数,我希望能够计算每月的总休假天数。我遇到了DatePart函数,它似乎与一个边缘情况不同:当日期范围跨越月边界时。由于DatePart函数返回一个给定日期的月份,我不再能够使用它来确定该边缘案例记录的休假天数(在上面的例子中它是记录3),因为它适用于两个单独的个月。

理想情况下,我希望我的决赛桌看起来像:

 Month       #OfDays
--------------------
   4           11               (1st record - 2, 2nd record - 7, 3rd record - 2)
   5           8                (3rd record - 3, 4th record - 5)

我考虑过一些混乱的选项,例如填充一个临时表,每个记录表示不同的一天,然后对其进行查询,但我不确定这与报告有什么关系。现在我的报告记录源是(不正确的)查询,是否可以将记录源作为返回记录源的VBA函数?

我想到的另一件事是可能有一个初始查询将任何边缘情况分成两个单独的记录,其中日期范围仅涵盖一个月,然后将其用于我的最终分组查询。这甚至可能吗?

我觉得这个问题可能有一个更简单的解决方案,但我看不到它。

如果有人有任何想法,我将不胜感激!

1 个答案:

答案 0 :(得分:2)

要使用Access查询完成任务,您需要创建一个名为[Numbers]的表,其中包含一个名为[n]的Number(Long Integer)列,其中包含数字1,2,3,......最高你期望与之合作的一年。我按如下方式创建了我的

   n
----
   1
   2
   3
 ...
2499
2500

您还需要将以下VBA功能粘贴到Access模块​​

Public Function IsValidDayOfYear(YearValue As Long, DayValue As Long) As Boolean
Dim IsLeapYear As Boolean
If (YearValue Mod 400) = 0 Then
    IsLeapYear = True
ElseIf (YearValue Mod 100) = 0 Then
    IsLeapYear = False
ElseIf (YearValue Mod 4) = 0 Then
    IsLeapYear = True
Else
    IsLeapYear = False
End If
IsValidDayOfYear = (DayValue <= IIf(IsLeapYear, 366, 365))
End Function

假设您的源表名为[DateRanges]。我们首先创建一个查询,该查询生成源表中表示的每年的每一天。这里的诀窍是DateSerial()“翻过”月边界,所以

DateSerial(2013,1,32)=#2013-02-01#

DateSerial(2013,1,234)=#2013-08-22#

SELECT DateSerial(yr.n, 1, dy.n) AS [Date]
FROM Numbers yr, Numbers dy
WHERE 
    (
        yr.n 
            BETWEEN (SELECT MIN(DatePart("yyyy", DateRanges.StartDate)) FROM DateRanges)
                AND (SELECT MAX(DatePart("yyyy", DateRanges.EndDate)) FROM DateRanges)
    )
    AND (dy.n < 367) AND IsValidDayOfYear(yr.n, dy.n)

对于您的示例数据,该查询将返回2013年的所有日期。

我们将该查询保存为[AllDays]。现在,我们可以使用它来提取每个日期范围的各个日期(省略StartDate,以便最终计数与您的问题相匹配)

SELECT [Date] FROM AllDays
WHERE EXISTS 
    (
        SELECT * FROM DateRanges 
        WHERE AllDays.[Date] BETWEEN DateAdd("d", 1, DateRanges.StartDate) AND DateRanges.EndDate
    )

返回与每个范围相对应的各个日期,即

Date      
----------
2013-04-14
2013-04-15
2013-04-18
2013-04-19
2013-04-20
2013-04-21
2013-04-22
2013-04-23
2013-04-24
2013-04-29
2013-04-30
2013-05-01
2013-05-02
2013-05-03
2013-05-06
2013-05-07
2013-05-08
2013-05-09
2013-05-10

我们可以将该查询保存为[RangeDays],然后使用它来按月计算我们的计数......

SELECT DatePart("m", [Date]) AS [Month], COUNT(*) AS NumOfDays
FROM RangeDays
GROUP BY DatePart("m", [Date])

...返回

Month  NumOfDays
-----  ---------
    4         11
    5          8