在SQL中具有条件的范围内查找最小日期

在SQL中具有条件的范围内查找最小日期,sql,Sql,我需要在相同的LegalStart/LegalEnd日期内按客户查找第一个StayStart,该日期>=8天LOS且仅为类型1。第1类连续停留时间少于8天的,如果加起来是8天,并且没有其他类型的中断,则计算在内。在这种情况下,我需要最早的连续停留。而这正是我无法理解的方法。将8岁以下但累计到8岁的连续住宿相加。我很想发布一些我尝试过的东西,但我一无所获。 这是我的桌子: cust# LegalStart StayStart StayEnd LegalEnd Type

我需要在相同的LegalStart/LegalEnd日期内按客户查找第一个StayStart,该日期>=8天LOS且仅为类型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个服务水平,因为之前的客户的行加起来是10天,但是,其中一个是2类,所以不算在内。另外两次住宿加起来是7天,所以不算在内。因此,2016年2月11日的StayStart(服务水平为17)是符合我标准的最短日期。 同一客户的第二个结果是在下一个法定日期时间段内,4天的停留时间加上接下来的2天和4天的停留时间均为1类,因此它们加起来超过或等于8天。因此,2016年3月2日的StayStart日期是符合我的标准的最小日期。 第三个结果行是下一个客户的结果行,这是正确的,因为该客户的所有4行都是类型1,加起来>=8天。因此,15年1月12日的StayStart是符合我的标准的最小日期。
感谢您提供的帮助。

这在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
;
此查询使用变量计算具有相同客户、法定日期范围和类型的组之间的运行总计。棘手的部分是,从内部查询返回的每一行实际上并不包含来自当前表行的任何数据,而是表示来自同一组中先前行的汇总日期的变量的内容。当客户、法定日期范围或类型发生更改时,@newGroup变量设置为TRUE,表示新组的开始。外部查询的where子句将从内部查询中选择一行,前提是该行被标记为新组,类型为1,总天数>=8

将存根行追加到查询的表格行,以触发对最终组的处理


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

这里有一个版本应该与支持分析功能的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
此查询按客户和法定日期范围将表行分组到分区中。每个组的最后一行由分区的结尾或type列的值的更改确定,这将触发要分配给名为g的输出列的当前总行号,否则g将保留为null

下一个查询将使用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

您正在使用哪些关系数据库管理系统:mysql、sql server、postgresql、oracle。。。?这些日期是存储为日期类型还是其他类型?您尝试过什么吗?应该是最大值而不是最小值。但是您可以使用sql server的秩或行号窗口函数,可能还有一些类型库,我想我已经在顶部添加了我正在使用sql server。当我必须找到第一个日期时,为什么会是max…,最老的,或最早的,等等?不,我没有尝试过任何b/c我写了这个代码只是为了找到min date,它是Thank you Mark。有几个问题。这是什么?:=ManagementStudio出错了,我在web上搜索MySQL命令时找不到它。MSSMS还告诉我没有名为date的函数,那么DATEADD呢?还是别的什么?我错了,我把你上面写的误读为MySQL而不是SQL Server。