2

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

    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”跨越多个学期,那么如何构建这样的查询呢?

在过去的几天里,我一直把头撞在墙上,我将非常感谢任何人能够提供的任何帮助......

非常感谢。

4

1 回答 1

2

您可以使用 生成一个学期内所有日期的列表,而不仅仅是查看学期的长度或它们之间的间隔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 演示

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

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

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

这是一个完整的查询(这里是SQLFiddle 演示):

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
于 2013-07-13T14:55:42.313 回答