【问题标题】:SQL -- computing end dates from a given start date with arbitrary breaksSQL - 从给定的开始日期计算结束日期,任意中断
【发布时间】:2013-07-13 12:23:03
【问题描述】:

我有一个可变长度的“学期”表,它们之间有可变中断,并且有一个约束,使得“开始日期”总是大于前一个“结束日期”:

    id   start_date    end_date
    -----------------------------
     1   2012-10-01   2012-12-20 
     2   2013-01-05   2013-03-28
     3   2013-04-05   2013-06-29
     4   2013-07-10   2013-09-20

还有如下的学生表,其中开始日期可能出现在给定学期内的任何时间:

   id    start_date  n_weeks
   -------------------------
    1    2012-11-15     25
    2    2013-02-12      8 
    3    2013-03-02     12 

我正在尝试通过加入“学期”中的“学生”来计算“结束日期”,其中考虑了学期之间的可变长度休息时间。

我可以画出上学期的结束日期(即从上一行的 end_date 开始),然后使用以下方法通过减法找到学期之间的天数:

    SELECT  start_date
          , end_date
          , lag(end_date) OVER () AS prev_end_date
          , start_date - lag(end_date) OVER () AS days_break  
    FROM terms 
    ORDER BY start_date;

显然,如果只有两个术语,只需在天数中添加“休息”(也许,转换为“周”)——从而将“结束日期”延长同一时期的时间。

但是如果给定学生的“n_weeks”跨越多个学期,那么如何构建这样的查询?

在过去的几天里,我一直在用头撞墙,如果有人能提供任何帮助,我将非常感激......

非常感谢。

【问题讨论】:

标签: sql postgresql date range


【解决方案1】:

您可以使用generate_series() 生成一个学期内所有日期的列表,而不仅仅是查看学期的长度或它们之间的间隔,如下所示:

SELECT
  row_number() OVER () as day_number,
  day
FROM
(
  SELECT
    generate_series(start_date, end_date, '1 day') as day
  FROM
    semesters
) as day_series
ORDER BY 
  day

(SQLFiddle demo)

这会为学期中的每一天分配一个任意但连续的“天数”,从而跳过学期之间的所有间隔。

然后您可以将其用作子查询/CTE JOINed 到您的学生表中:首先找到他们开始日期的“天数”,然后添加 7 * n_weeks 以找到“天数” “他们的结束日期,最后加入回来找到那个“天数”的实际日期。

这假设部分周不需要特殊处理 - 即如果n_weeks 为 4,则学生必须在学期期间注册 28 天。该方法可用于测量周数(将1 week 作为generate_series() 的最后一个参数传递),另外还有一个步骤是找出学生的start_date 属于哪一周。

这是一个完整的查询 (SQLFiddle demo here):

WITH semester_days AS
(
  SELECT
    semester_id,
    row_number() OVER () as day_number,
    day_date::date
  FROM
  (
    SELECT
      id as semester_id,
      generate_series(start_date, end_date, '1 day') as day_date
    FROM
      semesters
  ) as day_series
  ORDER BY 
    day_date
)
SELECT
  S.id as student_id,
  S.start_date,
  SD_start.semester_id as start_semester_id,
  S.n_weeks,
  SD_end.day_date as end_date,
  SD_end.semester_id as end_semester_id
FROM
  students as S
JOIN
  semester_days as SD_start
  On SD_start.day_date = S.start_date
JOIN
  semester_days as SD_end
  On SD_end.day_number = SD_start.day_number + (7 * S.n_weeks)
ORDER BY
  S.start_date

【讨论】:

  • 这简直太棒了。我(和我的初期溃疡)非常感谢你! (感谢您的详细解释——非常有用……)
猜你喜欢
  • 1970-01-01
  • 2012-04-06
  • 2020-08-21
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 2019-05-31
  • 1970-01-01
相关资源
最近更新 更多