在SQL查询中组合拆分日期范围

时间:2008-09-26 15:25:46

标签: sql database ms-access database-design

我正在处理一个需要根据日期范围组合一些数据行的查询。除日期范围被拆分外,这些行在所有数据值中都是重复的。例如,表格数据可能看起来像

StudentID   StartDate   EndDate     Field1  Field2
1           9/3/2007    10/20/2007  3       True
1           10/21/2007  6/12/2008   3       True
2           10/10/2007  3/20/2008   4       False
3           9/3/2007    11/3/2007   8       True
3           12/15/2007  6/12/2008   8       True

查询结果应该具有组合的拆分日期范围。查询应该将日期范围与仅一天的间隔组合在一起。如果间隔超过一天,则不应合并行。没有拆分日期范围的行应该保持不变。结果看起来像

StudentID   StartDate   EndDate     Field1  Field2
1           9/3/2007    6/12/2008   3       True
2           10/10/2007  3/20/2008   4       False
3           9/3/2007    11/3/2007   8       True
3           12/15/2007  6/12/2008   8       True

此查询的SELECT语句是什么?

10 个答案:

答案 0 :(得分:2)

以下代码应该有效。我做了一些假设如下:日期范围没有重叠,任何字段都没有NULL值,给定行的开始日期总是小于结束日期。如果您的数据不符合这些条件,则需要调整此方法,但它应指向正确的方向。

您可以使用子查询而不是视图,但这可能很麻烦,因此我使用视图使代码更清晰。

CREATE VIEW dbo.StudentStartDates
AS
    SELECT
        S.StudentID,
        S.StartDate,
        S.Field1,
        S.Field2
    FROM
        dbo.Students S
    LEFT OUTER JOIN dbo.Students PREV ON
        PREV.StudentID = S.StudentID AND
        PREV.Field1 = S.Field1 AND
        PREV.Field2 = S.Field2 AND
        PREV.EndDate = DATEADD(dy, -1, S.StartDate)
    WHERE PREV.StudentID IS NULL
GO

CREATE VIEW dbo.StudentEndDates
AS
    SELECT
        S.StudentID,
        S.EndDate,
        S.Field1,
        S.Field2
    FROM
        dbo.Students S
    LEFT OUTER JOIN dbo.Students NEXT ON
        NEXT.StudentID = S.StudentID AND
        NEXT.Field1 = S.Field1 AND
        NEXT.Field2 = S.Field2 AND
        NEXT.StartDate = DATEADD(dy, 1, S.EndDate)
    WHERE NEXT.StudentID IS NULL
GO


SELECT
    SD.StudentID,
    SD.StartDate,
    ED.EndDate,
    SD.Field1,
    SD.Field2
FROM
    dbo.StudentStartDates SD
INNER JOIN dbo.StudentEndDates ED ON
    ED.StudentID = SD.StudentID AND
    ED.Field1 = SD.Field1 AND
    ED.Field2 = SD.Field2 AND
    ED.EndDate > SD.StartDate AND
    NOT EXISTS (SELECT * FROM dbo.StudentEndDates ED2 WHERE ED2.StudentID = SD.StudentID AND ED2.Field1 = SD.Field1 AND ED2.Field2 = SD.Field2 AND ED2.EndDate < ED.EndDate AND ED2.EndDate > SD.StartDate)
GO

答案 1 :(得分:0)

根据我的经验,我必须在后处理中组合范围(不是在SQL中,而是在我的脚本中)。我不确定SQL是否可以执行此操作,特别是因为您无法确切知道在任何特定情况下需要链接多少个日期范围。如果可以做到这一点,我也很想知道。

编辑:我的回答是假设每位学生有多个日期,而不仅仅是开始和结束。如果您只有一个没有间隙的日期范围,那么其他提到的解决方案就可以了。

答案 2 :(得分:0)

SELECT StudentID, MIN(startdate) AS startdate, MAX(enddate), field1, field2
FROM tablex
GROUP BY StudentID, field1, field2

如果假设学生的时间范围之间没有差距,那将会产生结果。

答案 3 :(得分:0)

select StudentID, min(StartDate) StartDate, max(EndDate) EndDate, Field1, Field2 
  from table
 group by StudentID, Field1, Field2

答案 4 :(得分:0)

如果min()/ max()解决方案不够好(例如,如果日期不是连续的并且您想分别对不同的日期范围进行分组),我想知道使用Oracle的START WITH和CONNECT BY子句是否有效。当然,这对每个数据库都不起作用。

答案 5 :(得分:0)

编辑:为Access创建另一组SQL。我测试了所有这些,但一块一块,因为我不知道如何在Access中一次发表几个语句。由于我也不知道如何做评论,你可以在下面的SQL版本中看到评论。

select 
studentid, min(startdate) as Starter, max(enddate) as Ender, field1, field2, 
max(startDate) - Min(endDate)  as MaxGap 
into tempIDs
from student 
group by studentid, field1, field2 ;  

delete from tempIDs where MaxGap > 1;

UPDATE student INNER JOIN TempIDs ON Student.studentID = TempIDS.StudentID
SET Student.StartDate = [TempIDs].[Starter],
 Student.EndDate = [TempIDs].[Ender];

我认为就是这样,在SQL Server中 - 我没有在Access中这样做。我还没有测试它是否有重叠几个记录等奇特的条件,但这应该让你开始。它会更新所有重复的小间隙记录,并在数据库中留下额外内容。 MSDN有一个关于消除重复的页面:http://support.microsoft.com/kb/139444

select 
studentid, min(startdate) as StartDate, max(enddate) as EndDate, field1, field2, 
datediff(dd, Min(endDate),max(startDate)) as MaxGap 
into #tempIDs
from #student 
group by studentid, field1, field2    

-- Update the relevant records.  Keeps two copies of the massaged record 
-- - extra will need to be deleted.

update #student 
set startdate = #TempIDS.startdate, enddate = #tempIDS.EndDate
from #tempIDS 
where #student.studentid = #TempIDs.StudentID and MaxGap < 2

答案 6 :(得分:0)

您是否考虑过非equi加入?这看起来像这样:

SELECT A.StudentID, A.StartDate, A.EndDate, A.Field1, A.Field2
FROM tblEnrollment AS A LEFT JOIN tblEnrollment AS B ON (A.StudentID = B.StudentID) 
   AND (A.EndDate=B.StartDate-1)
WHERE B.StudentID Is Null;

给你的是所有没有相应记录的记录,这些记录从第一个记录的结束日期开始。

[警告:请注意,您只能在SQL视图中编辑Access查询设计器中的非equi连接 - 切换到设计视图可能会导致连接丢失(尽管如果您切换Access会告诉您有关问题的信息) ,如果你立即切换回SQL View,你就不会失去它)]

如果你那么UNION那个:

SELECT A.StudentID, A.StartDate, B.EndDate, A.Field1, A.Field2
FROM tblEnrollment AS A INNER JOIN tblEnrollment AS B ON (A.StudentID = B.StudentID) 
   AND (A.EndDate= B.StartDate-1)

假设一次只有两个以上的连续记录,它应该能满足您的需求。如果你有两个以上的连续记录(可能需要查看StartDate-1与EndDate相比),我不确定你是怎么做的,但这可能会让你开始朝着正确的方向前进。

答案 7 :(得分:0)

Tom H.在接受的答案中提供的另一个最终查询是

SELECT
    SD.StudentID,
    SD.StartDate,
    MIN(ED.EndDate),
    SD.Field1,
    SD.Field2
FROM
    dbo.StudentStartDates SD
INNER JOIN dbo.StudentEndDates ED ON
    ED.StudentID = SD.StudentID AND
    ED.Field1 = SD.Field1 AND
    ED.Field2 = SD.Field2 AND
    ED.EndDate > SD.StartDate
GROUP BY
    SD.StudentID, SD.Field1, SD.Field2, SD.StartDate

这也适用于所有测试数据。

答案 8 :(得分:0)

以下是使用SQL Server 2005/2008语法测试数据的示例。

DECLARE @Data TABLE(
    CalendarDate datetime )

INSERT INTO @Data( CalendarDate )
-- range start
SELECT '1 Jan 2010'
UNION ALL SELECT '2 Jan 2010'
UNION ALL SELECT '3 Jan 2010'
-- range start
UNION ALL SELECT '5 Jan 2010'
-- range start
UNION ALL SELECT '7 Jan 2010'
UNION ALL SELECT '8 Jan 2010'
UNION ALL SELECT '9 Jan 2010'
UNION ALL SELECT '10 Jan 2010'

SELECT DateGroup, Min( CalendarDate ) AS StartDate, Max( CalendarDate ) AS EndDate
FROM(   SELECT NextDay.CalendarDate, 
            DateDiff( d, RangeStart.CalendarDate, NextDay.CalendarDate ) - ROW_NUMBER() OVER( ORDER BY NextDay.CalendarDate ) AS DateGroup
        FROM( SELECT Min( CalendarDate ) AS CalendarDate
                FROM @data ) AS RangeStart
            JOIN @data AS NextDay
                ON NextDay.CalendarDate >= RangeStart.CalendarDate ) A
GROUP BY DateGroup

答案 9 :(得分:-1)

这是SQL(语言)中的典型问题,例如Joe Celko的书籍“聪明的SQL”(第23章,地区,运行,差距,序列和系列)以及他的最新着作“集思考”(第15章)。

虽然使用怪物查询在运行时修复数据很有趣,但对我而言,这是可以更好地离线和程序性修复的情况之一(我个人会在Excel电子表格中使用公式进行修复) )。

重要的是要建立有效的数据库约束,以防止重复发生的重叠期。再次,在SQL中编写顺序约束是一种经典:参见Snodgrass(http://www.cs.arizona.edu/people/rts/tdbbook.pdf)。提示MS Access用户:您需要使用CHECK约束。