在SQL中以Z周期动态容纳Y日期的X计数

时间:2016-08-15 07:33:14

标签: sql amazon-redshift

我有一个包含两列的简单表:emp_iddate。每当员工为当天打拳时,日期都会附加到此表格中。

在某些时间间隔内,我必须检查哪些员工在过去8周内每2周工作3天或更少。我的查询如下:

select x.emp_id
from
(
select
emp_id
, sum(case when tt.date >= dateadd('week', -8, '2016-08-15') and tt.date < dateadd('week', -6, '2016-08-15') then 1 else 0 end) as four_biweeks_ago
, sum(case when tt.date >= dateadd('week', -6, '2016-08-15') and tt.date < dateadd('week', -4, '2016-08-15') then 1 else 0 end) as three_biweeks_ago
, sum(case when tt.date >= dateadd('week', -4, '2016-08-15') and tt.date < dateadd('week', -2, '2016-08-15') then 1 else 0 end) as two_biweeks_ago
, sum(case when tt.date >= dateadd('week', -2, '2016-08-15') and tt.date < '2016-08-15' then 1 else 0 end) as last_biweek
from temps_timetable tt
where tt.date >= dateadd('week', -8, '2016-08-15')
) x
where x.four_biweeks_ago <= 3
and x.three_biweeks_ago <= 3
and x.two_biweeks_ago <= 3
and x.last_biweek <= 3

但是,我希望“升级”此查询并使其动态化,以适应过去几周的任何数量。由于此查询是从内部接口运行的,因此唯一的动态字段是引用日期(在本例中为2016-08-15)和每个最大日期数(3)。我可以通过用户界面使过去双周的数量变得灵活,但我不知道如何升级查询以处理超过4 sum行,因为这应该更改查询本身。例如,将其设置为过去10周意味着我必须添加sum(case when tt.date >= dateadd('week', -10, '2016-08-15') and tt.date < dateadd('week', -8, '2016-08-15') then 1 else 0 end) as five_biweeks_ago。别名无所谓,顺便说一句。

我试着阅读PIVOT来处理这个问题,但逻辑目前正在逃避我。

TL; DR:如何更改查询以便我可以动态容纳过去Z周内每Y周的X个日期?

1 个答案:

答案 0 :(得分:1)

转动表格的关键是使用floor()(请参阅Redshift documentation)并将其与datediff()相结合,这样可以轻松按给定时间间隔进行分区,即每两周一次。

因此,这应该会产生与查询相同的结果:

with _reference as (
  select
      '2016-08-15'::date as date
     , 8::int            as weeks_ago  -- Z
     , 2::float          as partition  -- Y
     , 3::int            as max_count  -- X
),
_stats as (
    select
        emp_id
        /** If datediff=3 and partition=2, this returns 1: */
      , floor(datediff(week, temps_timetable.date, _reference.date) / _reference.partition) as partition_in_past
      , count(distinct temps_timetable.date)
    from
        _reference, temps_timetable
    where
        datediff(week, temps_timetable.date, _reference.date) <= _reference.weeks_ago
    and
        temps_timetable.date < _reference.date
    group by
        emp_id
      , partition_in_past
)
select emp_id
from _stats, _reference
group by emp_id
having sum(case when _stats.count > _reference.max_count then 1 else 0 end) = 0
;