在SQL条件中查找最小日期

时间:2018-01-29 19:31:44

标签: sql

我需要在同一LegalStart / LegalEnd日期内找到第一个StayStart,其中> = 8天(LOS)且仅为Type 1。类型1的连续停留少于8天,如果它们加起来最多8天并且没有其他类型中断它们。在那种情况下,我需要最早的连续住宿。这就是我无法弄清楚如何处理的问题。添加8个以下但连续8个的连续住宿。我很想发布我尝试过的东西,但是我已经无处可去了。
这是我的表:

cust# LegalStart    StayStart   StayEnd    LegalEnd       Type  LOS(days) 
1000    5/3/2013    2/1/2016    2/5/2016    11/18/2016        1       4
1000    5/3/2013    2/5/2016    2/8/2016    11/18/2016        1       3
1000    5/3/2013    2/8/2016    2/11/2016   11/18/2016        2       3
1000    5/3/2013    2/11/2016   2/28/2016   11/18/2016        1      17
1000    3/2/2016    3/2/2016    3/5/2016    11/18/2016        1       4
1000    3/2/2016    3/5/2016    3/7/2016    11/18/2016        1       2
1000    3/2/2016    3/7/2016    3/11/2016   11/18/2016        1       4
1000    3/2/2016    3/12/2016   3/22/2016   11/18/2016        1      10
2000    3/1/2011    12/1/2015   12/3/2015   1/8/2016          1       2
2000    3/1/2011    12/3/2015   12/5/2015   1/8/2016          1       2
2000    3/1/2011    12/5/2015   12/6/2015   1/8/2016          1       1
2000    3/1/2011    12/6/2015   12/18/2015  1/8/2016          1      12

预期结果

cust#   LegalStart  StayStart   StayEnd     LegalEnd     Type   LOS(days)
1000    5/3/2013    2/11/2016   2/28/2016   11/18/2016     1      17
1000    3/2/2016    3/2/2016    3/5/2016    11/18/2016     1       4
2000    3/1/2011    12/1/2015   12/3/2015   1/8/2016       1       2

我的第一个结果是具有17个LOS的结果,因为之前该客户的行确实累计最多10天,其中一个保留是类型2,因此它不计算在内。其他2次入住最多可累计7天,因此不计算在内。因此,2016年11月11日StaySart(LOS为17)是符合我标准的最短日期 同一个cust#的第二个结果是在下一个法定日期时间范围内,4天的停留加上接下来的2天和4天的2次停留都是类型1,因此他们加起来超过或者= 8天。因此,2016年3月2日的StayStart日期是符合我标准的最短日期 第三个结果行是下一个客户,这是正确的,因为该客户的所有4行都是类型1,并且加起来> = 8天。因此,2015年12月1日的StayStart是符合我标准的最短日期 感谢您提供的任何帮助。

2 个答案:

答案 0 :(得分:0)

这在MySQL中有点乱,它缺乏对分析函数的支持,但可以使用变量来完成,如下所示(假设你的表名为“theTable”):

select custNum, legalStart, legalEnd, stayStart, stayEnd, type, losDays from (
  select
    @custNum as custNum,
    @legalStart as legalStart,
    @legalEnd as legalEnd,
    @stayStart as stayStart,
    @stayEnd as stayEnd,
    @type as type,
    @losDays as losDays,
    @newGroup :=
      (@custNum != custNum
        or @legalStart != legalStart
        or @legalEnd != legalEnd
        or @type != type)
      as newGroup,
    @aggLosDays as groupAggLosDays,
    @aggLosDays :=
        case when @newGroup
        then losDays
        else @aggLosDays + losDays end as __________,
    case when @newGroup then @losDays := losDays else null end as _,
    @custNum :=
        case when @custNum != custNum
        then custNum else @custNum end as ___,
    @legalStart :=
        case when @legalStart != legalStart
        then legalStart else @legalStart end as ____,
    @legalEnd :=
        case when @legalEnd != legalEnd
        then legalEnd else @legalEnd end as _____,
    @type :=
        case when @type != type
        then type else @type end as ______,
    @stayStart :=
        case when @newGroup
        then stayStart else @stayStart end as _______,
    @stayEnd :=
        case when @newGroup
        then stayEnd else @stayEnd end as ________
  from
    (
      (
        select
            custNum, legalStart, legalEnd, stayStart, stayEnd,
            type, datediff(stayEnd,stayStart) as losDays
        from theTable
        order by custNum, legalStart, legalEnd, stayStart, type
      ) union all (
          select -1, date('0000-00-00'), date('0000-00-00'),
              date('0000-00-00'), date('0000-00-00'), -1, null
      )
    ) tt
    join (select @aggLosDays := -1) z1
    join (select @custNum := -1) z2
    join (select @legalStart := date('0000-00-00')) z3
    join (select @legalEnd := date('0000-00-00')) z4
    join (select @stayStart := date('0000-00-00')) z5
    join (select @staylEnd := date('0000-00-00')) z6
    join (select @type := -1) z7
    join (select @losDays := null) z8
  order by
    custNum,
    legalStart,
    legalEnd,
    stayStart,
    stayEnd,
    type,
    losDays
) z
where
  newGroup
  and type=1
  and groupAggLosDays >= 8
;

此查询使用变量计算具有相同客户,法定日期范围和类型的组之间的运行总计。棘手的部分是从内部查询返回的每一行实际上不包含来自当前“theTable”行的任何数据,而是包含表示同一组中先前行的汇总日期的变量内容。当客户,合法日期范围或类型发生更改时,@ newGroup变量设置为TRUE,表示新组的开始。外部查询的where子句将从内部查询中选择一行(如果它被标记为新组),其类型为1且总天数> = 8。

“stub”行被追加到查询的“theTable”行,以触发最终组的处理。

MySQL不是我的主要RDBMS,所以我相信这可以变得更干净。

答案 1 :(得分:0)

这是一个适用于支持分析功能的MS SQL Server版本的版本。

select distinct custNum, legalStart, stayStart, stayEnd, legalEnd, type, losDays
from (
  select
    custNum, legalStart, legalEnd, type,
    first_value(stayStart) over (partition by gg order by stayStart) as stayStart,
    first_value(stayEnd) over (partition by gg order by stayEnd) as stayEnd,
    first_value(losDays) over (partition by gg order by stayEnd) as losDays,
    sum(losDays) over (partition by gg order by stayStart) as accLosDays
  from (
    select
      custNum, legalStart, legalEnd, stayStart, stayEnd, type, losDays,
      case when g is not null
        then g
        else min(g) over (
          partition by custNum, legalStart, legalEnd
          rows between current row and unbounded following)
        end as gg
    from (
      select 
        custNum, legalStart, legalEnd, stayStart, stayEnd, type,
        datediff(day,stayStart,stayEnd) as losDays,
        case
          when lead(type, 1, -1)
            over (partition by custNum, legalStart, legalEnd order by stayStart) != type 
          then row_number() over ()
          else null end as g
      from
        theTable
      order by
        custNum,
        legalStart,
        legalEnd,
        stayStart,
        type
    ) z
  ) zz
  where
    type=1
) zzz
where
  accLosDays >= 8;

为了理解它是如何工作的,我建议运行每个select语句,从最里面开始并向外工作:

select 
  custNum, legalStart, legalEnd, stayStart, stayEnd, type,
  datediff(day,stayStart,stayEnd) as losDays,
  case
    when lead(type, 1, -1)
      over (partition by custNum, legalStart, legalEnd order by stayStart) != type 
    then row_number() over ()
    else null end as g
from
  theTable
order by
  custNum,
  legalStart,
  legalEnd,
  stayStart,
  type

此查询按行和法定日期范围将表行分组到分区中。每个组的最后一行(由分区末尾或类型列值的更改确定)触发当前整体行号分配给名为g的输出列,否则g保留为null。 / p>

下一个查询“up”将使用g set的值获取这些行,并发出一个新列gg,该列填充g的空值以及其组的正确值:

select
  custNum, legalStart, legalEnd, stayStart, stayEnd, type, losDays,
  case
    when g is not null
    then g
    else min(g) over (
      partition by custNum, legalStart, legalEnd
      rows between current row and unbounded following
    ) end as gg
from
  <<<<inner query>>>>

最后,将第一个stayStart,stayEnd和losDays值拉出结果,按类型和每个组的总天数进行过滤:

select distinct
  custNum, legalStart, stayStart, stayEnd, legalEnd, type, losDays
from (
  select
    custNum, legalStart, legalEnd, type,
    first_value(stayStart)
      over (partition by gg order by stayStart) as stayStart,
    first_value(stayEnd)
      over (partition by gg order by stayEnd) as stayEnd,
    first_value(losDays)
      over (partition by gg order by stayEnd) as losDays,
    sum(losDays)
      over (partition by gg order by stayStart) as accLosDays
  from (
    <<<<inner query>>>>
  where
    type=1
) zzz
where
  accLosDays >= 8