聚合/窗口函数查找顺序行的最小值和最大值

时间:2015-08-20 18:36:59

标签: sql sql-server sql-server-2012

我有一个SQL表格,我想找到一组记录的第一个和最后一个日期,只要它们按顺序排列。

 Patient | TestType | Result |     Date
------------------------------------------
    1    |    1     |   A    |  2012-03-04
    1    |    1     |   A    |  2012-08-19
    1    |    1     |   B    |  2013-05-27
    1    |    1     |   A    |  2013-06-20
    1    |    2     |   X    |  2012-08-19
    1    |    2     |   X    |  2013-06-20
    2    |    1     |   B    |  2014-09-09
    2    |    1     |   B    |  2015-04-19

应该以

的形式返回
Patient | TestType | Result | StartDate   |   EndDate
--------------------------------------------------------
   1    |    1     |   A    |  2012-03-04 | 2012-08-19 
   1    |    1     |   B    |  2013-05-27 | 2013-05-27
   1    |    1     |   A    |  2013-06-20 | 2013-06-20 
   1    |    2     |   X    |  2012-08-19 | 2013-06-20
   2    |    1     |   B    |  2014-09-09 | 2015-04-19

问题在于,如果我只按PatientTestTypeResult进行分组, 那么上面例子中的第一行和第三行将成为一行。

Patient | TestType | Result | StartDate   |   EndDate
--------------------------------------------------------
   1    |    1     |   A    |  2012-03-04 | 2013-06-20 
   1    |    1     |   B    |  2013-05-27 | 2013-05-27
   1    |    2     |   X    |  2012-08-19 | 2013-06-20
   2    |    1     |   B    |  2014-09-09 | 2015-04-19

我觉得我可以做一个聪明的事情,我可以做一个分区,但我不能弄清楚它是什么。

3 个答案:

答案 0 :(得分:2)

有几种方法可以解决这个问题。我喜欢使用行号值的差异来识别组:

select patient, testtype, result,
       min(date) as startdate, max(date) as enddate
from (select t.*, 
             (row_number() over (partition by patient, testtype order by date) -
              row_number() over (partition by patient, testtype, result order by date)
             ) as grp
      from table t
     ) t
group by patient, testtype, result, grp
order by patient, startdate;

答案 1 :(得分:0)

select patient, testtype, result, date as startdate, 
isnull(lead(date) over(partition by patient, testtype, result order by date), date) as enddate
from tablename;

您可以使用lead函数从每个组的下一行获取日期值(作为enddate)。

带有示例数据的

SQL Fiddle

答案 2 :(得分:0)

看看这是否能满足您的需求。

with T1 as (
  select
    *,
    case when lag(Patient,1)
           over (order by Patient, TestType, Result) = Patient
          and lag(TestType,1) 
           over (order by Patient, TestType, Result) = TestType
          and lag(Result,1)
           over (order by Patient, TestType, Result) = Result
    then null else 1 end as Changes
  from t
), T2 as (
  select
    Patient,
    TestType,
    Result,
    dt,
    sum(Changes) over (
      order by Patient, TestType, Result, dt
    ) as seq
  from T1
)
  select
    Patient,
    TestType,
    Result,
    min(dt) as dtFrom,
    max(dt) as dtTo
  from T2
  group by Patient, TestType, Result, seq
  order by Patient, TestType, Result