4

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

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 语句是什么?

4

10 回答 10

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
于 2008-09-26T16:38:16.943 回答
0

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

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

于 2008-09-26T15:28:25.350 回答
0
SELECT StudentID, MIN(startdate) AS startdate, MAX(enddate), field1, field2
FROM tablex
GROUP BY StudentID, field1, field2

假设这不是学生时间范围之间的差距,那将产生结果。

于 2008-09-26T15:28:58.483 回答
0
select StudentID, min(StartDate) StartDate, max(EndDate) EndDate, Field1, Field2 
  from table
 group by StudentID, Field1, Field2
于 2008-09-26T15:28:59.093 回答
0

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

于 2008-09-26T16:06:41.093 回答
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
于 2008-09-26T16:19:38.753 回答
0

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

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 查询设计器中编辑非等连接 - 切换到设计视图可能会导致连接丢失(尽管如果您确实切换 Access 会告诉您问题,如果您立即切换回 SQL 视图,您不会丢失它)]

如果您然后将其与以下内容联合:

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 相比),但这可能会让你朝着正确的方向开始。

于 2008-09-26T19:31:45.843 回答
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

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

于 2008-09-26T19:56:31.897 回答
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
于 2010-05-20T22:12:47.330 回答
-1

这是 SQL(语言)中的一个经典问题,例如 Joe Celko 的著作“Smarties 的 SQL”(第 23 章,区域、运行、间隙、序列和系列)和他的最新著作“集合中的思考”(第 15 章)。

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

重要的是设置有效的数据库约束,以防止重复出现重叠时段。同样,用 SQL 编写序列约束是一个经典:参见 Snodgrass ( http://www.cs.arizona.edu/people/rts/tdbbook.pdf )。MS Access 用户提示:您需要使用 CHECK 约束。

于 2008-09-29T12:36:17.953 回答