在Access SQL中加入相交日期范围

时间:2015-01-11 16:53:37

标签: sql ms-access

我有两个表中包含日期范围,两者之间不一定匹配。我希望得到一个结果,它结合两个表之间的日期范围(和值)。以下是表格中数据的示例:

Table A                         Table B
+----------+----------+-------+ +----------+----------+-------+
|StartDate |  EndDate | Hours | |StartDate |  EndDate | Hours |
+----------+----------+-------+ +----------+----------+-------+
|11/02/2014|11/06/2014|  38.75| |11/02/2014|11/02/2014|   7.75|
|12/06/2014|12/10/2014|  23.25| |11/03/2014|11/03/2014|   7.75|
                                |11/04/2014|11/04/2014|   7.75|
                                |11/05/2014|11/05/2014|   7.75|
                                |11/06/2014|11/06/2014|   7.75|
                                |12/09/2014|12/15/2014|  15.50|

查询的结果应如下所示:

Results
+----------+----------+-------+-------+
|StartDate |  EndDate |A Hours|B Hours|
+----------+----------+-------+-------+
|11/02/2014|11/06/2014|  38.75|  38.75|
|12/06/2014|12/15/2014|  23.25|  15.50|

为了使我正在做的更清楚,这里有一些例子:

Table A                         Table B
+----------+----------+-------+ +----------+----------+-------+
|StartDate |  EndDate | Hours | |StartDate |  EndDate | Hours |
+----------+----------+-------+ +----------+----------+-------+
|09/01/2014|09/01/2014|   7.75| |09/02/2014|09/02/2014|   7.75|

Results
+----------+----------+-------+-------+
|StartDate |  EndDate |A Hours|B Hours|
+----------+----------+-------+-------+
|09/01/2014|09/01/2014|   7.75|   Null|
|09/02/2014|09/02/2014|   Null|   7.75|

Table A                         Table B
+----------+----------+-------+ +----------+----------+-------+
|StartDate |  EndDate | Hours | |StartDate |  EndDate | Hours |
+----------+----------+-------+ +----------+----------+-------+
|08/02/2014|08/02/2014|   7.75| |08/01/2014|08/05/2014|  38.75|
|08/05/2014|08/09/2014|  23.25| |08/08/2014|08/08/2014|   7.75|
|          |          |       | |08/15/2014|08/16/2014|  15.50|

Results
+----------+----------+-------+-------+
|StartDate |  EndDate |A Hours|B Hours|
+----------+----------+-------+-------+
|08/01/2014|08/09/2014|  31.00|  46.50|
|08/15/2014|08/16/2014|   Null|  15.50|

基本上它归结为我正在尝试建立两个表中可比较日期范围的小时数的详细比较。

到目前为止,我最大的问题是尝试找到一种方法对日期范围的结果进行分组,当结果中每条记录的StartDate和EndDate可能来自表A或表B时,具体取决于具体情况。

3 个答案:

答案 0 :(得分:0)

如果开始日期在B的开始日期和结束日期之间,那么您的逻辑似乎是A条记录。

  select A.StartDate, A.EndDate, A.Hours, sum(B.Hours) as bhours
  from A inner join
       B
       on b.startDate >= a.startDate and b.StartDate <= a.endDate
  group by A.StartDate, A.EndDate A.Hours

答案 1 :(得分:0)

好吧,似乎没有一个简单的解决方案,所以我最终提出了一个复杂的解决方案,查询每个可能的场景,并与UNIONs一起使用。

首先,我编写了一个查询,检索了两个表之间完美匹配的所有日期范围

接下来,由于Access不允许FULL OUTER JOIN,我编写了一个LEFT OUTER JOIN查询和一个RIGHT OUTER JOIN查询,它从每个表中检索所有日期范围,与其他表中的日期范围没有任何交集。我加入了表格

(a.StartDate BETWEEN b.StartDate
                 AND b.EndDate
 OR a.EndDate BETWEEN b.StartDate
                  AND b.EndDate)

然后在where子句中仅指定NULL值。

最后,如果有多个范围一起运行,我必须得到所有相交的日期范围并合并它们,而没有使用递归CTE的好处。我基本上使用外部联接和MIN和MAXing将开始和结束日期嵌套在同一子查询的五个版本中。

答案 2 :(得分:0)

第一步是使用子查询来查找每个段startDate和endDate。

   select segments.startDate as StartDate, 
            segments.endDate as EndDate,
            sum(A.hours) as AHours,
            sum(B.hours) as BHours
            from (
                select startDate, 
                       min(endDate) as endDate
                from (
                      select distinct starts.startDate
                            from (select A.startDate
                                 from A
                                 union all 
                                 select B.startDate
                                 from b) as starts
                            where not exists 
                                  (select 1
                                   from A
                                   where A.startDate < starts.startDate
                                         and starts.startDate <= A.endDate)
                              and not exists 
                                  (select 1
                                   from B
                                   where B.startDate < starts.startDate
                                         and starts.startDate <= B.endDate)
                            ) as segmentStarts
                inner join (
                           select distinct ends.endDate
                           from (select A.endDate
                                 from A
                                 union all 
                                 select B.endDate
                                 from b) as ends
                           where not exists 
                                 (select 1
                                  from A
                                  where A.endDate > ends.endDate
                                        and ends.endDate >= A.startDate)
                                  and not exists 
                                 (select 1
                                  from B
                                  where B.endDate > ends.endDate
                                        and ends.endDate >= B.startDate) 
                            ) as segmentEnds
                on segmentStarts.startDate <= segmentEnds.endDate
                group by segmentStarts.startDate            
                ) as segments
            left join A
            on segments.startDate <= A.endDate
            and A.startDate <= segments.endDate
            left join B
            on segments.startDate <= B.endDate
            and B.startDate <= segments.endDate
            group by segments.startDate, segments.endDate
            order by segments.startDate