SQL 返回最近日期和下一个日期的课程列表

Posted

技术标签:

【中文标题】SQL 返回最近日期和下一个日期的课程列表【英文标题】:SQL return list of courses with most recent date and next date 【发布时间】:2013-11-04 10:52:22 【问题描述】:

我在一个 SQL 数据库中有两个表; 'course' 和 'event',课程表有 courseID (pk)、coursecode 和 coursetitle 字段。事件表有 eventID (pk) courseID (fk)、startdate、enddate。

我需要使用事件表的开始日期返回所有课程的列表,其中有一列显示它发生的最后日期,以及它应该发生的下一个日期。

我可以在事件表中获取最近的日期,或特定课程的下一个日期,如下所示:

SELECT TOP 1
    startdate as nextdate    
FROM event

WHERE 
    CONVERT(VARCHAR(8),startdate,112) > CONVERT(VARCHAR(8), GETDATE(), 112)
    AND coursecode = 'acc01'

ORDER BY startdate ASC

我希望结果如下所示:

+------------+-------------+------------+-------------+------------+-------------+
| coursecode | coursetitle | lastevent  | lasteventid | nextevent  | nexteventid |
+------------+-------------+------------+-------------+------------+-------------+
| acc01      | Access      | 2012-09-30 | 20127       | 2013-12-10 | 35612       | 
| wrd37      | Word        | 2013-11-02 | 34816       | NULL       | NULL        |
+------------+-------------+------------+-------------+------------+-------------+

我需要显示每个课程代码一次(课程来自不同的供应商,因此课程代码可能不是唯一的字段,因此是 courseid 字段)。课程可能没有过去的日期、未来的日期或任何日期。在这些日期/eventid 字段中使用 Null 很好,因为我将在报告设计中对它们进行格式化。

我希望我不是在重复一个问题,但到目前为止我还没有在这里找到任何东西。

提前感谢大家的帮助

【问题讨论】:

什么是 sql 风格? @geomagas 2008 R2 SP2 多个coursecodes 也将有多个coursetitles 每个coursecode。标题选择的标准是什么? @geomagas 不太清楚你的意思,课程标题和课程代码的组合应该是唯一的,但是表中有一个单独的 CourseID 字段,它是主键,这是用作连接的。在最终输出中,我可以将 CourseID、CourseCode 和 Coursetitle 显示为前 3 列 想象一下(coursecode,coursetitle) 组合('acc01','Access foo')('acc01','Access bar')。如果查询只返回一次'acc01',它会选择哪个标题? 【参考方案1】:

您可以使用OUTER APPLY 实现此目的:

WITH CourseCodeEvents AS
(   SELECT  c.CourseCode, e.EventID, e.StartDate, e.EndDate
    FROM    Course c
            INNER JOIN Event e
                ON e.CourseID = c.CourseID
), DistinctCourseCode AS
(   SELECT  DISTINCT CourseCode, CourseTitle
    FROM    Course
)
SELECT  c.CourseCode,
        c.CourseTitle,
        LastEvent = LastEvent.StartDate,
        LastEventID = LastEvent.EventID,
        NextEvent = NextEvent.StartDate,
        NextEventID = NextEvent.EventID
FROM    DistinctCourseCode c
        OUTER APPLY
        (   SELECT  TOP 1 EventID, StartDate, EndDate
            FROM    CourseCodeEvents e
            WHERE   e.CourseCode = c.CourseCode
            AND     e.StartDate > CAST(GETDATE() AS DATE)
            ORDER BY StartDate ASC
        ) NextEvent
        OUTER APPLY
        (   SELECT  TOP 1 EventID, StartDate, EndDate
            FROM    CourseCodeEvents e
            WHERE   e.CourseCode = c.CourseCode
            AND     e.StartDate <= CAST(GETDATE() AS DATE)
            ORDER BY StartDate DESC
        ) LastEvent;

注意,我也改了这个:

CONVERT(VARCHAR(8),startdate,112) > CONVERT(VARCHAR(8), GETDATE(), 112)

StartDate < CAST(GETDATE() AS DATE)

将日期转换为 varchars 然后比较它们不是删除时间元素的有效方法。转换为 DATE 效率更高(如果您有 2008+)。对于其他版本的 sql-server 中的其他方法,请阅读this article。

【讨论】:

非常感谢,虽然我还没有完全测试过,但这似乎可行。我的日期转换混乱的原因是因为我们数据库中的日期字段是整数,例如今天日期的20131104。我对 SQL 很陌生,所以转换日期字符串可能是我的混乱尝试,所以我可以得到一个日期来比较。【参考方案2】:

根据您使用的 sql 的风格,尝试类似:

;WITH cteEventsLast
AS
(
    SELECT e.CourseID, 
           MAX(e.StartDate) AS StartDate
    FROM Event e
    WHERE e.StartDate < GETDATE()
    GROUP BY e.CourseID
),

WITH cteEventsNext
AS
(
    SELECT e.CourseID, 
           MIN(e.StartDate) AS StartDate
    FROM Event e
    WHERE e.StartDate >= GETDATE()
    GROUP BY e.CourseID
)

SELECT c.CourseCode,
       c.CourseTitle,
       el.StartDate AS LastEvent,
       en.StartDate AS NextEvent
FROM Course c LEFT OUTER JOIN
     cteEventsLast el ON (el.CourseID = c.CourseID) LEFT OUTER JOIN
     cteEventsNext en ON (en.CourseID = c.CourseID)

此示例使用公用表表达式,您可以将其用于 TSQL / MSSQL 服务器。但根据您的 SQL 风格,您可能需要使用稍微不同的 SQL

如果您不能使用 CTE,则可以将 cteEventsLast 和 cteEventsNext 作为 FROM 子句中的子查询

我也不会用

CONVERT(VARCHAR(8),startdate,112) > CONVERT(VARCHAR(8), GETDATE(), 112)

比较日期,就像您的问题一样。而是直接比较日期,即像这样

startdate > GETDATE()

【讨论】:

OP 专门要求每个CourseCode 只出现一次。

以上是关于SQL 返回最近日期和下一个日期的课程列表的主要内容,如果未能解决你的问题,请参考以下文章

Linq to sql,聚合列,按日期分组到列表视图

SQL时间转换脚本查询列表

SQL 选择除最近日期之外的所有内容

SQL - 创建两个日期之间的自定义日期列表

SQL列表在两个日期之间重复发生日期,结束日期为下一个开始日期

在记录列表中选择最近的日期,但小于今天的日期