【问题标题】:Grouping users by 90 day periods in SQL在 SQL 中按 90 天的时间段对用户进行分组
【发布时间】:2011-09-02 04:30:59
【问题描述】:

我需要获取过去 4 周内每一周被解雇的最后 90 名员工。这是一个滚动报告。

员工表是这样的:

id      hired_date       term_date
3        07/1/2011        09/01/2011
4        07/18/2011        NULL    (NULL means still active) 
5        01/20/2009       08/23/2011
6        05/30/2011       8/22/2011
7        7/20/2011        7/23/2011

报告将采用这种格式。 ID #4,5 将被忽略,因为 #4 仍处于活动状态且 #5 在 90 天期限之前被雇用。

Week ending     Terminated employees hired within past 90 days
09/03/2011        2   --(id 3,7)
8/27/2011         2   --(id 6,7)
..
..
  • 所以 9/03 的第一行是雇用的终止员工人数 2011 年 9 月 3 日过去 90 天(可追溯到 2011 年 6 月 5 日)。这不包括 id #6,因为员工是在 6 月 5 日之前被雇用的。
  • 8/27 周的第二行相同,但范围是 8/27 到 5/29。

我有一个日期表,但它只包含 week_start_date、week_end_date 和 week_number。我是否需要创建一个包含 90 天的时间段?

我不知道如何仅在过去 90 天内为员工执行此操作,然后计算过去 4 周的每一周。

SQL Server 2008

编辑: 我想我很接近了。我现在正在测试它。 All_termed_employees 是任何日期范围内被解雇的员工的列表。 week 表现在包含与其关联的每个星期的 Ninety_begin_date 和 Ninety_end_date。

select wk.ninety_end, count(h.id)
FROM @weeks wk
LEFT JOIN all_termed_employees h 
    ON h.hire_date <= wk.ninety_end and h.hire_date >= wk.ninety_begin
          and .termination_date <= wk.ninety_end AND h.termination_date >= wk.ninety_begin
ORDER BY d.id
GROUP BY wk.ninety_end

【问题讨论】:

    标签: sql sql-server sql-server-2008


    【解决方案1】:
    ;WITH n(n) AS 
          -- just 4 rows - makes it easy to extend to 5 weeks, 6 weeks, etc.
          ( SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ),
    
    d(dt) AS
          -- single row with the end of the current week
          -- this could be a variable but I get a lot of flack for not inlining
          ( SELECT dt = CONVERT(DATE, DATEADD(DAY, 
              7-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP))),
    
    w(dt) AS 
          -- get the end of each week based on the rows in n
          ( SELECT DATEADD(WEEK, -n.n, d.dt) FROM n CROSS JOIN d )
    
    SELECT 
        w.dt, SUM(CASE 
        WHEN e.term_date >= DATEADD(DAY, -90, w.dt) 
        AND e.term_date  <  DATEADD(DAY, 1,   w.dt) 
        AND e.hired_date >= DATEADD(DAY, -90, w.dt) 
        AND e.hired_date <  DATEADD(DAY, 1,   w.dt)  
        THEN 1 ELSE 0 END)
    FROM dbo.Employees AS e
    CROSS JOIN w
    GROUP BY w.dt
    ORDER BY w.dt DESC; 
    

    【讨论】:

    • @Aaron_Bertrand 感谢您的编辑。这让我更清楚了。是的,这似乎确实可以满足我的需求。 w.[start] 是指 w.[dt] 吗?
    • 是的,已更正,抱歉。我原计划使用一周的[开始],并在飞行中更改。
    • 在临时表上使用 CTE 有什么特别的原因吗?当我需要做一些我想利用的初步操作/计算时,我通常会使用临时表……我想知道是否有充分的理由使用 CTE 代替它们。谢谢。
    • 只是它使它成为一个语句而不是一堆不同的步骤,如果我不必创建一个 #temp 表来写 4 行,为什么还要麻烦?如果我确实首先要对行进行大量处理,或者遇到无法正确优化 CTE 的问题(有很多这样的情况),我经常会恢复到 #temp 表。对于具有常量的 CTE,我更喜欢上面的 #temp 表 100:1。就像我在评论中所说的那样,我有时也会因为声明变量而感到沮丧,但在生产代码中我可能会这样做,而不是将 CTE 称为 d...
    • 没有人不想看到 DECLARE @dt DATE = CURRENT_TIMESTAMP; 只是为了稍后在查询中使用它 - 他们宁愿我只是将常量放入并避免“额外的代码行”(即使有转换和/或变量重复多次)。这没什么大不了的,只是我注意到的一些事情被反复强调。
    【解决方案2】:

    这样的事情怎么样?

    EMP 模拟您的员工表。 PAST_FOUR_WEEKS 是一个简单的 4 行内存表,用于标识报告期间。然后我计算雇用日期和任期日期之间的差异,如果它小于或等于 90,我们将其计为 1(达到阈值)或 0)。然后,我将在给定时间段内达到的所有阈值相加

    ; WITH EMP (id, hired_date, term_date) AS
    (
        select 3, CAST('2011-07-01' AS datetime), CAST('2011-09-01' AS datetime)
        union all select 4, '2011-06-18', null
        union all select 5, '01/20/2009','08/23/2011'
        union all select 6, '05/30/2011','8/22/2011'
        union all select 7, '7/20/2011','7/23/2011'
    )
    , PAST_FOUR_WEEKS (period, period_rank) AS
    (
        -- magic goes here to determine end of week
        SELECT CURRENT_TIMESTAMP, 1
        UNION ALL SELECT dateadd(week, -1, CURRENT_TIMESTAMP) , 2
        UNION ALL SELECT dateadd(week, -2, CURRENT_TIMESTAMP) , 3
        UNION ALL SELECT dateadd(week, -3, CURRENT_TIMESTAMP) , 4
    )
    , HIRED_TERMED_DIFF AS
    (
        SELECT
            *
        ,   DATEDIFF(d, E.hired_date, coalesce(E.term_date, '9999-12-31T23:59:59.997')) AS duration
        ,   CASE WHEN DATEDIFF(d, E.hired_date, coalesce(E.term_date, '9999-12-31T23:59:59.997')) < 91 THEN 1 ELSE 0 END AS threshold_met
        FROM
            EMP E 
    )
    SELECT
        PFW.period
    ,   SUM(HTD.threshold_met) AS [Terminated employees hired within past 90 days]
    FROM 
        PAST_FOUR_WEEKS PFW
        LEFT OUTER JOIN
            HIRED_TERMED_DIFF HTD
            ON HTD.hired_date BETWEEN DATEADD(day, -90, PFW.period) AND PFW.period
    GROUP BY
        PFW.period
    ORDER BY
        1 DESC
    ,   2
    

    样本输出

    period                    Terminated employees hired within past 90 days
    2011-09-01 14:46:29.243   2
    2011-08-25 14:46:29.243   3
    2011-08-18 14:46:29.243   3
    2011-08-11 14:46:29.243   3
    

    【讨论】:

    • 谢谢!这很有帮助。但终止计数应仅列出过去 90 天内聘用且在过去 90 天内被聘用的人员(在同一日期范围内聘用和终止)。所以它不应该计算在 2009 年被雇用并最近被任命的人。我认为您的查询不应该计算那些?
    • @James 我认为如果您包含更多的样本数据行并说明为什么每行应该或不应该每周计算,这将有助于澄清。我认为我的查询满足了您的要求,但只有您可以确定地告诉我们,因为我们不知道您的表格中有什么以及您期望的答案是什么。
    猜你喜欢
    • 2019-07-05
    • 2016-04-02
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    相关资源
    最近更新 更多