SQL:查找连续几天没有的行组

时间:2018-03-19 01:47:24

标签: sql sql-server sql-server-2016

我的Microsoft SQL Server 2016中有以下Attendance表:

ID         StudentID  Date          AbsenceReasonID
----------------------------------------------------
430957     10158      2018-02-02    2   
430958     10158      2018-02-03    2   
430959     10158      2018-02-04    11  
430960     12393      2018-03-15    9   
430961     1          2018-03-15    9   
430962     12400      2018-03-15    9   
430963     5959       2018-03-15    11  

我希望有一个查询可以检索一组行,其中Date列连续发生了3个或更多个缺席,仅供单个学生(StudentID)使用。理想情况下,运行查询后的以下数据将是

ID         StudentID  Date          AbsenceReasonID
----------------------------------------------------
430957     10158      2018-02-02    2   
430958     10158      2018-02-03    2   
430959     10158      2018-02-04    11  

请注意,如果学生在星期五缺席,我希望在周末到周一(无视周末日期)进行。

如果需要更多信息以更好地帮助您协助我,请告诉我。我使用了以下查询作为启动器,但知道它不是我要找的:

SELECT 
    CONVERT(datetime, A.DateOF, 103),
    A.SchoolNum, EI.FullName,
    COUNT(A.SchoolNum) as 'Absences'
FROM 
    Attendance A
INNER JOIN 
    EntityInformation EI ON EI.SchoolNum = A.SchoolNum AND EI.Deleted = 0
INNER JOIN 
    Enrolment E ON EI.SchoolNum = E.SchoolNum AND E.Deleted = 0
GROUP BY 
    A.SchoolNum, A.DateOf, FullName
HAVING 
    COUNT(A.SchoolNum) > 1
    AND A.DateOf = GETDATE()
    AND A.SchoolNum in (SELECT SchoolNum FROM Attendance A1 
                        WHERE A1.DateOf = A.DateOf -7)

这是一个静态解决方案,用于检索学生的ID在过去7天内发生过两次的缺席。这既不是连续的,也不是三天或更多天。

3 个答案:

答案 0 :(得分:0)

如果你需要在一段时间内得到缺席(让我们说过去7天),那么你可以做这样的事情

 SELECT 
    ID,
    StudentID,
    [Date], 
    AbsenceReasonID
FROM(
SELECT 
    ID,
    StudentID,
    [Date], 
    AbsenceReasonID, 
    COUNT(StudentID) OVER(PARTITION BY StudentID ORDER BY StudentID) AS con, 
    ((DATEPART(dw, [Date]) + @@DATEFIRST) % 7) AS dw
FROM attendance
) D
WHERE 
     D.con > 2
AND [Date] >= '2018-02-02'
AND [Date] <= GETDATE()
AND dw NOT IN(0,1)

并根据您的给定数据输出

|     ID | StudentID |       Date | AbsenceReasonID |
|--------|-----------|------------|-----------------|
| 430957 |     10158 | 2018-02-02 |               2 |

您可以根据需要调整输出。

SQL Fiddle

答案 1 :(得分:0)

试试这个:

CTE包含学生在前一天和后一天(周末除外)缺席的缺席日期。最后的2 UNION会添加每组的第一个和最后一个,并消除重复项。

with cte(id, studentId, dateof , absenceReasonId)
as
(
select a.* 
from attendance a
where exists (select 1 from attendance preva
              where preva.studentID = a.studentID
              and   datediff(day, preva.dateof, a.dateof)
                    <= (case when datepart(dw, preva.dateof) >= 5
                        then 8 - datepart(dw, preva.dateof)
                        else 1 
                        end)
              and preva.dateof < a.dateof)
and exists (select 1 from attendance nexta
              where nexta.studentID = a.studentID
              and   datediff(day, a.dateof, nexta.dateof)
                    <= (case when datepart(dw, a.dateof) >= 5
                        then 8 - datepart(dw, a.dateof)
                        else 1 
                        end)
              and nexta.dateof > a.dateof))              

select cte.*
from cte
union  -- use union to remove duplicates
select preva.* 
from
attendance preva
inner join
cte
on preva.studentID = cte.studentID
and preva.dateof < cte.dateof
and datediff(day, preva.dateof, cte.dateof)
                    <= (case when datepart(dw, preva.dateof) >= 5
                        then 8 - datepart(dw, preva.dateof)
                        else 1 
                        end) 
union
select nexta.*
from attendance nexta
inner join
cte
on nexta.studentID = cte.studentID
and   datediff(day, cte.dateof, nexta.dateof)
       <= (case when datepart(dw, cte.dateof) >= 5
                then 8 - datepart(dw, cte.dateof)
                else 1 
            end)
and nexta.dateof > cte.dateof  
order by studentId, dateof 

sqlfiddle

答案 2 :(得分:0)

您可以使用它来查找缺席范围。在这里,我使用递归CTE对几年内的所有日期进行编号,同时记录他们的工作日。然后使用另一个递归CASE WHEN来加入同一个学生的缺席日期,考虑周末应该被跳过(阅读连接条款的SET DATEFIRST 1 -- Monday = 1, Sunday = 7 ;WITH Days AS ( -- Recursive anchor: hard-coded first date SELECT GeneratedDate = CONVERT(DATE, '2017-01-01') UNION ALL -- Recursive expression: all days until day X SELECT GeneratedDate = DATEADD(DAY, 1, D.GeneratedDate) FROM Days AS D WHERE DATEADD(DAY, 1, D.GeneratedDate) <= '2020-01-01' ), NumberedDays AS ( SELECT GeneratedDate = D.GeneratedDate, DayOfWeek = DATEPART(WEEKDAY, D.GeneratedDate), DayNumber = ROW_NUMBER() OVER (ORDER BY D.GeneratedDate ASC) FROM Days AS D ), AttendancesWithNumberedDays AS ( SELECT A.*, N.* FROM Attendance AS A INNER JOIN NumberedDays AS N ON A.Date = N.GeneratedDate ), AbsenceSpree AS ( -- Recursive anchor: absence day with no previous absence, skipping weekends SELECT StartingAbsenceDate = A.Date, CurrentDateNumber = A.DayNumber, CurrentDateDayOfWeek = A.DayOfWeek, AbsenceDays = 1, StudentID = A.StudentID FROM AttendancesWithNumberedDays AS A WHERE NOT EXISTS ( SELECT 'no previous absence date' FROM AttendancesWithNumberedDays AS X WHERE X.StudentID = A.StudentID AND X.DayNumber = CASE A.DayOfWeek WHEN 1 THEN A.DayNumber - 3 -- When monday then friday (-3) WHEN 7 THEN A.DayNumber - 2 -- When sunday then friday (-2) ELSE A.DayNumber - 1 END) UNION ALL -- Recursive expression: find the next absence day, skipping weekends SELECT StartingAbsenceDate = S.StartingAbsenceDate, CurrentDateNumber = A.DayNumber, CurrentDateDayOfWeek = A.DayOfWeek, AbsenceDays = S.AbsenceDays + 1, StudentID = A.StudentID FROM AbsenceSpree AS S INNER JOIN AttendancesWithNumberedDays AS A ON S.StudentID = A.StudentID AND A.DayNumber = CASE S.CurrentDateDayOfWeek WHEN 5 THEN S.CurrentDateNumber + 3 -- When friday then monday (+3) WHEN 6 THEN S.CurrentDateNumber + 2 -- When saturday then monday (+2) ELSE S.CurrentDateNumber + 1 END ) SELECT StudentID = A.StudentID, StartingAbsenceDate = A.StartingAbsenceDate, EndingAbsenceDate = MAX(N.GeneratedDate), AbsenceDays = MAX(A.AbsenceDays) FROM AbsenceSpree AS A INNER JOIN NumberedDays AS N ON A.CurrentDateNumber = N.DayNumber GROUP BY A.StudentID, A.StartingAbsenceDate HAVING MAX(A.AbsenceDays) >= 3 OPTION (MAXRECURSION 5000) )。最后显示每个缺席狂欢连续N天过滤。

SELECT
    StudentID = A.StudentID,
    StartingAbsenceDate = A.StartingAbsenceDate,
    EndingAbsenceDate = MAX(N.GeneratedDate),
    AbsenceDays = MAX(A.AbsenceDays)
FROM
    AbsenceSpree AS A
    INNER JOIN NumberedDays AS N ON A.CurrentDateNumber = N.DayNumber
GROUP BY
    A.StudentID,
    A.StartingAbsenceDate
HAVING
    MAX(A.AbsenceDays) >= 3

如果要列出原始出勤表行,可以替换最后一个选择:

CTE + SELECT

使用此, FilteredAbsenceSpree AS ( SELECT StudentID = A.StudentID, StartingAbsenceDate = A.StartingAbsenceDate, EndingAbsenceDate = MAX(N.GeneratedDate), AbsenceDays = MAX(A.AbsenceDays) FROM AbsenceSpree AS A INNER JOIN NumberedDays AS N ON A.CurrentDateNumber = N.DayNumber GROUP BY A.StudentID, A.StartingAbsenceDate HAVING MAX(A.AbsenceDays) >= 3 ) SELECT A.* FROM Attendance AS A INNER JOIN FilteredAbsenceSpree AS F ON A.StudentID = F.StudentID WHERE A.Date BETWEEN F.StartingAbsenceDate AND F.EndingAbsenceDate OPTION (MAXRECURSION 5000)

 $(document).ready(function(){
        $.ajax({
            type: "POST",
            url: "https://api.rss2json.com/v1/api.json?rss_url=https://yourblog/feed/",

            success: function(data, textStatus) {
                var j=1;
                for(var i=0;i<data.items.length;i++){
                 your functionalities;
} } }); 
});