Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/83.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 基于连续日期查找最近日期_Sql_Sql Server_Greatest N Per Group - Fatal编程技术网

Sql 基于连续日期查找最近日期

Sql 基于连续日期查找最近日期,sql,sql-server,greatest-n-per-group,Sql,Sql Server,Greatest N Per Group,我有一张表,列出了所有员工的缺勤(假期),我们想知道的是今天谁不在,以及他们回来的日期 不幸的是,缺勤没有ID,因此如果其中一个日期是今天,您不能仅从缺勤ID检索最大日期 但是,缺勤在输入时每天都会有一个递增的ID,因此我需要一个查询,如果有一个条目包含今天的日期,则会找到employeeID,然后递增缺勤ID列以查找缺勤的最大日期 表格示例(假设今天的日期为2014年11月11日,英国格式): 因此,根据上述情况,我们希望返回日期为: EmployeeID ReturnDate 10

我有一张表,列出了所有员工的缺勤(假期),我们想知道的是今天谁不在,以及他们回来的日期

不幸的是,缺勤没有ID,因此如果其中一个日期是今天,您不能仅从缺勤ID检索最大日期

但是,缺勤在输入时每天都会有一个递增的ID,因此我需要一个查询,如果有一个条目包含今天的日期,则会找到employeeID,然后递增缺勤ID列以查找缺勤的最大日期

表格示例(假设今天的日期为2014年11月11日,英国格式):

因此,根据上述情况,我们希望返回日期为:

EmployeeID     ReturnDate
10             15/11/2014
21             12/11/2014
05             11/11/2014
编辑:请注意,140-143范围不能包含在结果中,因为它们将在将来出现,并且缺勤的日期范围都不在今天


大概我需要在每个条目上运行一个迭代子函数,其中包含employeeID匹配的今天的日期。

因此,根据我相信您提出的问题,您希望根据您在系统中记录的假期返回一份今天休假的人员列表,以及他们预计何时返回的列表,只能在连续几天内工作

架构设置

CREATE TABLE EmployeeAbsence
    ([AbsenceID] int, [EmployeeID] int, [AbsenceDate] DATETIME)
;

INSERT INTO EmployeeAbsence
    ([AbsenceID], [EmployeeID], [AbsenceDate])
VALUES
    (100, 10, '2014-11-11'),
    (101, 10, '2014-11-12'),
    (102, 10, '2014-11-13'),
    (103, 10, '2014-11-14'),
    (104, 10, '2014-11-15'),
    (107, 21, '2014-11-11'),
    (108, 21, '2014-11-12'),
    (120, 05, '2014-11-11'),
    (130, 15, '2014-11-20')
;
;WITH cte AS (
    SELECT EmployeeID, AbsenceDate
    FROM dbo.EmployeeAbsence
    WHERE AbsenceDate = CAST(GETDATE() AS DATE)
    UNION ALL
    SELECT  e.EmployeeID, e.AbsenceDate
    FROM cte
    INNER JOIN dbo.EmployeeAbsence e ON e.EmployeeID = cte.EmployeeID 
           AND e.AbsenceDate = DATEADD(d,1,cte.AbsenceDate)
    )
SELECT cte.EmployeeID, MAX(cte.AbsenceDate) 
FROM cte
GROUP BY cte.EmployeeID
| EMPLOYEEID |                     Return Date |
|------------|---------------------------------|
|          5 | November, 11 2014 00:00:00+0000 |
|         10 | November, 15 2014 00:00:00+0000 |
|         21 | November, 12 2014 00:00:00+0000 |
生成输出的递归CTE

CREATE TABLE EmployeeAbsence
    ([AbsenceID] int, [EmployeeID] int, [AbsenceDate] DATETIME)
;

INSERT INTO EmployeeAbsence
    ([AbsenceID], [EmployeeID], [AbsenceDate])
VALUES
    (100, 10, '2014-11-11'),
    (101, 10, '2014-11-12'),
    (102, 10, '2014-11-13'),
    (103, 10, '2014-11-14'),
    (104, 10, '2014-11-15'),
    (107, 21, '2014-11-11'),
    (108, 21, '2014-11-12'),
    (120, 05, '2014-11-11'),
    (130, 15, '2014-11-20')
;
;WITH cte AS (
    SELECT EmployeeID, AbsenceDate
    FROM dbo.EmployeeAbsence
    WHERE AbsenceDate = CAST(GETDATE() AS DATE)
    UNION ALL
    SELECT  e.EmployeeID, e.AbsenceDate
    FROM cte
    INNER JOIN dbo.EmployeeAbsence e ON e.EmployeeID = cte.EmployeeID 
           AND e.AbsenceDate = DATEADD(d,1,cte.AbsenceDate)
    )
SELECT cte.EmployeeID, MAX(cte.AbsenceDate) 
FROM cte
GROUP BY cte.EmployeeID
| EMPLOYEEID |                     Return Date |
|------------|---------------------------------|
|          5 | November, 11 2014 00:00:00+0000 |
|         10 | November, 15 2014 00:00:00+0000 |
|         21 | November, 12 2014 00:00:00+0000 |

CREATE TABLE EmployeeAbsence
    ([AbsenceID] int, [EmployeeID] int, [AbsenceDate] DATETIME)
;

INSERT INTO EmployeeAbsence
    ([AbsenceID], [EmployeeID], [AbsenceDate])
VALUES
    (100, 10, '2014-11-11'),
    (101, 10, '2014-11-12'),
    (102, 10, '2014-11-13'),
    (103, 10, '2014-11-14'),
    (104, 10, '2014-11-15'),
    (107, 21, '2014-11-11'),
    (108, 21, '2014-11-12'),
    (120, 05, '2014-11-11'),
    (130, 15, '2014-11-20')
;
;WITH cte AS (
    SELECT EmployeeID, AbsenceDate
    FROM dbo.EmployeeAbsence
    WHERE AbsenceDate = CAST(GETDATE() AS DATE)
    UNION ALL
    SELECT  e.EmployeeID, e.AbsenceDate
    FROM cte
    INNER JOIN dbo.EmployeeAbsence e ON e.EmployeeID = cte.EmployeeID 
           AND e.AbsenceDate = DATEADD(d,1,cte.AbsenceDate)
    )
SELECT cte.EmployeeID, MAX(cte.AbsenceDate) 
FROM cte
GROUP BY cte.EmployeeID
| EMPLOYEEID |                     Return Date |
|------------|---------------------------------|
|          5 | November, 11 2014 00:00:00+0000 |
|         10 | November, 15 2014 00:00:00+0000 |
|         21 | November, 12 2014 00:00:00+0000 |
说明:

CTE中的第一个
选择
,将使用此筛选器获取今天休假的员工:

WHERE AbsenceDate = CAST(GETDATE() AS DATE)
然后,此结果集通过与
EmployeeID
以及
缺勤日期
+1天匹配的联接重新联合到
employeedisease
表,以使用以下方法递归查找连续的天数:

-- add a day to the cte.AbsenceDate from the first SELECT
e.AbsenceDate = DATEADD(d,1,cte.AbsenceDate) 
最后的
SELECT
只需按员工将cte结果与每个员工计算的
MAX
缺勤日期分组

SELECT cte.EmployeeID, MAX(cte.AbsenceDate) 
FROM cte
GROUP BY cte.EmployeeID
周末除外:

我已经根据您的评论做了一个快速测试,下面对CTE中的
内部连接的修改应该排除周末,如果它检测到增加一天将导致星期六,则增加额外的天数:

INNER JOIN dbo.EmployeeAbsence e ON e.EmployeeID = cte.EmployeeID 
       AND e.AbsenceDate = CASE WHEN datepart(dw,DATEADD(d,1,cte.AbsenceDate)) = 7 
                                THEN DATEADD(d,3,cte.AbsenceDate) 
                           ELSE DATEADD(d,1,cte.AbsenceDate) END

因此,当您添加一天时:
datepart(dw,DATEADD(d,1,cte.AbsenceDate))=7
,如果结果是星期六(7),那么您需要添加3天而不是1天来获得星期一:
DATEADD(d,3,cte.AbsenceDate)
您需要做一些事情来将此数据转换为可用格式。您需要能够确定团队的开始和结束位置。这个示例很难做到这一点,因为没有直接的分组列

为了计算组的开始和结束时间,您需要创建一个包含所有列的CTE,并使用
LAG()
从每一行的前一行中获取
AbsenceID
EmployeeID
。在这个CTE中,您还应该同时使用
行编号()
,这样我们就可以将行重新排序为相同的顺序

比如:

WITH
    [AbsenceStage] AS (
        SELECT [AbsenceID], [EmployeeID], [AbsenceDate]
            ,[RN] = ROW_NUMBER() OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
            ,[AbsenceID_Prev] = LAG([AbsenceID]) OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
            ,[EmployeeID_Prev] = LAG([EmployeeID]) OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
        FROM [HR_Absence]
    )
   [EmployeeID_Prev] IS NULL -- We have a new group if the previous row is null
OR [EmployeeID_Prev] <> [EmployeeID] -- Or if the previous row is for a different employee
OR [AbsenceID_Prev] <> ([AbsenceID]-1) -- Or if the AbsenceID is not sequential
....
FROM [AbsenceStage] AS [Row]
INNER JOIN [AbsenceStage] AS [First]
    ON ([First].[RN] = (
        -- Get the first row before ([RN] Less that or equal to) this one where it is the start of a grouping
        SELECT MAX([RN]) FROM [AbsenceStage]
        WHERE [RN] <= [Row].[RN] AND (
               [EmployeeID_Prev] IS NULL
            OR [EmployeeID_Prev] <> [EmployeeID]
            OR [AbsenceID_Prev] <> ([AbsenceID]-1)
        )
    ))
...
现在我们有了这些,我们可以将每一行与前一行进行比较,以查看当前行是否与前一行处于不同的“组”中

这种情况类似于:

WITH
    [AbsenceStage] AS (
        SELECT [AbsenceID], [EmployeeID], [AbsenceDate]
            ,[RN] = ROW_NUMBER() OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
            ,[AbsenceID_Prev] = LAG([AbsenceID]) OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
            ,[EmployeeID_Prev] = LAG([EmployeeID]) OVER (ORDER BY [EmployeeID] ASC, [AbsenceDate] ASC, [AbsenceID] ASC)
        FROM [HR_Absence]
    )
   [EmployeeID_Prev] IS NULL -- We have a new group if the previous row is null
OR [EmployeeID_Prev] <> [EmployeeID] -- Or if the previous row is for a different employee
OR [AbsenceID_Prev] <> ([AbsenceID]-1) -- Or if the AbsenceID is not sequential
....
FROM [AbsenceStage] AS [Row]
INNER JOIN [AbsenceStage] AS [First]
    ON ([First].[RN] = (
        -- Get the first row before ([RN] Less that or equal to) this one where it is the start of a grouping
        SELECT MAX([RN]) FROM [AbsenceStage]
        WHERE [RN] <= [Row].[RN] AND (
               [EmployeeID_Prev] IS NULL
            OR [EmployeeID_Prev] <> [EmployeeID]
            OR [AbsenceID_Prev] <> ([AbsenceID]-1)
        )
    ))
...
然后,您可以将所有这些内容放到一个视图中,该视图为您提供
员工ID
,以及每次缺勤的开始和结束日期。然后,您可以使用以下工具轻松地提取员工当前的休假:

WHERE CAST(CURRENT_TIMESTAMP AS date) BETWEEN [Absence_Begin] AND [Absence_End]

就像这里的另一个答案一样,我将创建休假间隔,但使用不同的方法。首先是守则:

declare @today date = getdate(); --use whatever date here
with g as (
    select *, dateadd(day, -1 * row_number() over (partition by employeeid order by absencedate), AbsenceDate) as group_number
    from employeeabsence
) , leave_intervals as (
    select employeeid, min(absencedate) as [start], max(absencedate) as [end]
    from g
    group by EmployeeID, group_number
)
select employeeid, [start], [end]
from leave_intervals
where @today between [start] and [end]

作为解释,我们首先将日期值放入变量中。我选择了今天,但此代码将适用于传入的任何日期。接下来,我们创建一个公共表表达式(CTE),它将在表中添加一个分组列。这是溶液的肉,所以需要一些处理。在给定的时间间隔内,缺勤日期以每行一天的速度增加<代码>行数()
也以每行一个的速率增加。因此,如果我们从缺勤日期减去一个
行数()
天数,我们将得到另一个(任意)日期。这里的关键是要认识到任意日期对于间隔中的每一行都是相同的,因此我们可以使用它来分组。从那里开始,这只是一个做这件事的问题;获取每个间隔的最小值和最大值。最后,我们找出哪些区间包含@today。

请澄清问题中的规则,因为它们目前令人困惑。到目前为止,您编写了什么SQL?如何使用今天的日期?好的,那么这个表包含了人们的计划假期。本表列出了所有员工已预订的所有未来假日/假期,以及因病缺勤。我们所追求的是一个针对内部网的查询,可以显示今天不在办公室的其他员工,以及他们将返回的日期。因此,上面的示例列出了几次预订的假期,10号员工今天缺席,因此我们需要在内部网上显示这一点,但是2015年的预订,我们对今天不感兴趣。不是我的DB,因此我无法更改设计:(好吧,这真是太棒了,但我刚刚发现我的想法有一个小小的缺陷,那就是它没有忽略周末。所以,假设有人周一到周五休了整整两周的假,如果在他们离开的第一周进行上述操作,那么周六就可以作为第二天休息。不过,我想你已经给了我很多开始的时间了——谢谢!@Loic查看我的报告。)最后编辑以排除周末。它没有经过广泛测试,但类似的东西可能会起作用。