繁体   English   中英

有没有一种方法可以使用SET操作在SQL中汇总可变日期范围

[英]Is there a way to aggregate a variable range of dates in SQL using a SET operation

我有一张这样的桌子。

CREATE TABLE AbsentStudents
(
    Id int not null primary key identity(1,1),
    StudentId int not null,
    AbsentDate datetime not null
)

这是一个非常大的表格,每位学生缺席的每一天都有一行。

我被要求编写一个存储过程,以按日期范围获取学生缺勤的信息。 使此查询棘手的原因是我必须按“缺席情节”进行过滤/汇总。 构成“缺席发作”的天数是一个过程参数,因此可以有所不同。

因此,例如,我需要获取2016年1月1日至2016年1月17日之间缺席的学生列表,但前提是他们缺席的天数不超过@Days(2天或3天或任何参数指示的天数)。

我认为只有我自己才能弄清楚。 但是,在该日期范围内,一个学生可以有多个“缺席事件”。 因此,一个学生可能在日期范围的开始缺席了3天,在日期范围的中间缺席了2天,在日期范围的末尾缺席了4天,而每一个都构成了一个不同的“缺席事件”。 假设我的@Days参数为2,则该学生应返回3行。 并且,返回的每一行都应计算出该缺席事件中学生缺席的天数。

所以我想我的程序需要3个参数(@StartDate datetime,@ EndDate datetime,@ Days int)并返回类似这样的内容...

StudentId,InitialAbsentDate,ConcecutiveDaysMissed

理想情况下,它将使用SET操作并避免出现游标。 (尽管游标是唯一的选择,但它很好。)

更新(Shnugo提供)

测试场景

DECLARE @AbsentStudents TABLE(
    Id int not null primary key identity(1,1),
    StudentId int not null,
    AbsentDate datetime not null
);
INSERT INTO @AbsentStudents VALUES
--student 1
 (1,{d'2016-10-01'}),(1,{d'2016-10-02'}),(1,{d'2016-10-03'}) --three days 
,(1,{d'2016-10-05'}) --one day
,(1,{d'2016-10-07'}),(1,{d'2016-10-08'}) --two days
--student 2
,(2,{d'2016-10-01'}),(2,{d'2016-10-02'}),(2,{d'2016-10-03'}),(2,{d'2016-10-04'}) --four days
,(2,{d'2016-10-08'}),(2,{d'2016-10-09'}),(2,{d'2016-10-10'}) --three days
,(2,{d'2016-10-12'}); --one day

DECLARE @startDate DATETIME={d'2016-10-01'};
DECLARE @endDate DATETIME={d'2016-10-31'};
DECLARE @Days INT = 3;

如果您只是想缺勤一段时间,可以使用不同的行号方法来做到这一点。

现在,以下假设日期是连续的 ,没有间隔,并使用行号的差值来获取缺勤时间:

select student_id, 
       min(AbsentDate), 
       max(AbsentDate), 
       count(*) as number_of_days
from (select a.*,
             row_number() over (partition by student_id order by AbsentDate) as seqnum_sa
      from AbsentStudents a
     ) a
group by student_id, 
         dateadd(day, - seqnum_sa, AbsentDate);

笔记:

  • 您对最小日期和日期范围有其他要求。 这些可以通过where子句轻松处理。
  • 我怀疑您对避免周末休假有隐藏的要求。 这个(也没有其他答案)都不能解决这个问题。 另一个问题,如果这是一个问题。

您可以尝试以下查询:

SELECT
    StudentId
    , MIN(AbsentDate) AS InitialDate
    , COUNT(*) AS ConsecutiveDaysMissed
FROM (
SELECT 
    dateNumber - ROW_NUMBER() OVER(PARTITION BY StudentId ORDER BY dateNumber) AS PeriodId
    , AbsentDate
    , StudentId
FROM(
        SELECT
            StudentId
            , AbsentDate
            , CAST(CONVERT(CHAR(8), AbsentDate, 112) AS INT) AS dateNumber
        FROM AbsentStudents
        WHERE AbsentDate BETWEEN @StartDate AND @EndDate
    ) AS T
) AS StudentPeriod
GROUP BY StudentID, PeriodId

好了,您可以制作一个包含日期及其订单号的表格,而无需节假日和周末。 然后按日期与AbsentStudents进行联接,并使用订单号代替CAST(CONVERT(CHAR(8),AbsentDate,112)AS INT)AS dateNumber。

您可以使用技巧。 如果按日期排序,则可以通过从最小的元素中减去天数并添加一个每行递增一个的计数器来查找日期组。

SELECT StudentID 
FROM (
  SELECT StudentID, GROUP_NUM, COUNT(*) AS GROUP_DAY_CNT
  FROM (
    SELECT StudentId,
           DATEDIFF(dd,DATEADD(dd,M.Min, ROW_NUMBER() OVER (ORDER BY  AbsetntDate),AbsentDate) as GROUP_NUM
    FROM AbsentStudent
    CROSS JOIN (SELECT MIN(AbsentDate) as Min FROM AbsentStudents WHERE  AbsentDate BETWEEN @StartDate AND @EndDate) M
    WHERE AbsentDate BETWEEN @StartDate AND @EndDate
  ) X
  GROUP BY  StudentID, GROUP_NUM
) Z
WHERE GROUP_DAY_CNT >= @Days

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM