Self Join 计算给定日期前一天

Self Join to calcuate one day before a given date

我有这个 table Employee_Hire,其中有部门员工 (ID) 的雇用日期。现在,我想编写一个 Select 查询,其中再添加一列 (Last_day) 以显示他在该部门工作的最后一天。例如,对于部门 D01 中的 ID = 1,他的最后一天是 2014/12/31。同样在 D02 中,他的最后一天是 2017/12/31。

现在,由于他仍在 D04 工作,我们可以使用 'Till Present' 之类的字符串或 2099/12/31 之类的未来日期或任何符合此目的的日期。

我想为 table 中的每位员工执行此操作。

+---+-------------+------------+
| Id| Dept_name   | Hiredate   |
+---+-------------+------------+
| 1 |      D01    | 2012-01-01 |
| 1 |      D02    | 2015-01-01 |
| 1 |      D03    | 2018-01-01 |
| 1 |      D04    | 2019-01-01 |
| 2 |      D01    | 2010-01-01 |
| 2 |      D02    | 2012-01-01 |
| 3 |      D01    | 2008-01-01 |
| 3 |      D02    | 2010-01-01 |
| 3 |      D03    | 2012-01-01 |
| 4 |      D01    | 2015-01-01 |
| 4 |      D02    | 2017-01-01 |
+---+-------------+------------+

我知道这可以使用 LEAD OR LAG 函数来完成,但是谁能告诉我如何使用 Self Join 来完成吗?

提前致谢!!

您可以使用 lead() 和日期算法:

select
    t.*,
    dateadd(
        day, 
        -1, 
        lead(hiredate, 1, '2100-01-01') over(partition by id order by hiredate)
    ) last_day
from mytable t

lead() 的第二个参数是偏移量(这里,你想要 "next" 行,所以 1),第三个是默认值,返回给分区中的 "last" 行。

如果您想在没有 window 函数的情况下执行此操作,我建议使用横向连接或子查询:

select t.*, x.last_day
from mytable t
cross apply (
    select dateadd(
        day, 
        -1, 
        coalesce(min(t1.hiredate), 2100-01-01')
    ) last_day
    from mytable t1
    where t1.id = t.id and t1.hiredate > t.hiredate
)

假设 ROW_NUMBER() 函数也没有限制,此查询使用实际的自连接:

SELECT
    curr.Id,
    curr.Dept_name,
    curr.Hiredate,
    DATEADD(day, -1, next.Hiredate) AS Last_day
FROM
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY HireDate) AS RowNum
    FROM EmployeeHire
) AS curr
LEFT OUTER JOIN
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY HireDate) AS RowNum
    FROM EmployeeHire
) AS next
    ON curr.ID = next.ID AND curr.RowNum = next.RowNum - 1

我不确定在没有 ROW_NUMBER() 函数的情况下按任意条件排序时如何进行自连接。

我认为通常我会使用 window 函数,但如果 table 不适用并且您的数据没有太多列,则自连接与分组相结合可以看起来很干净:

select      a.id, a.Dept_name, a.Hiredate, 
            last_day = dateadd(day, -1, min(b.Hiredate))
from        @hireData a
left join   @hireData b on a.Id = b.Id and b.Hiredate > a.Hiredate
group by    a.id, a.Dept_name, a.Hiredate