MSSQL:考虑日期范围的DISTINCT

时间:2015-11-09 11:02:59

标签: sql sql-server distinct

我有一张看起来更像这样的表:

 K_PKEY          D_FROM               D_TO                 PERC
============ ==================== ==================== ===========
0013         01-JAN-2009 00:00:00 31-JUL-2011 00:00:00
0013         01-AUG-2011 00:00:00 31-DEC-2011 00:00:00
0013         01-JAN-2012 00:00:00 31-MAR-2012 00:00:00
0013         01-APR-2012 00:00:00 31-DEC-2012 00:00:00   75.000000
0013         01-JAN-2013 00:00:00 31-JAN-2013 00:00:00   50.000000
0013         01-FEB-2013 00:00:00 28-FEB-2013 00:00:00   50.000000
0013         01-MAR-2013 00:00:00 31-AUG-2013 00:00:00   75.000000
0013         01-SEP-2013 00:00:00 31-MAY-2015 00:00:00   75.000000
0013         01-JUN-2015 00:00:00 31-DEC-2100 00:00:00

我正在尝试构建一个考虑特定日期范围的DISTICT查询。

这就是我提出的:

SELECT DISTINCT k_pkey, MIN(d_from), MAX(d_to), perc FROM my_table GROUP BY k_pkey

它没有按照我想要的方式工作,我理解为什么。 MIN()和MAX()结合DISTINCT全局工作,这对于那种类型的查询是很自然的。这导致:

K_PKEY           D_FROM               D_TO                 PERC
============ ==================== ==================== ===========
0013         01-JAN-2009 00:00:00 31-DEC-2100 00:00:00
0013         01-APR-2012 00:00:00 31-MAY-2015 00:00:00   75.000000
0013         01-JAN-2013 00:00:00 28-FEB-2013 00:00:00   50.000000

我想要达到的目标是按时间顺序排列并且只将(可以说)彼此相邻的范围组合在一起。

K_PKEY           D_FROM               D_TO                 PERC
============ ==================== ==================== ===========
0013         01-JAN-2009 00:00:00 31-MAR-2012 00:00:00
0013         01-APR-2012 00:00:00 31-DEC-2012 00:00:00   75.000000
0013         01-JAN-2013 00:00:00 28-FEB-2013 00:00:00   50.000000
0013         01-MAR-2013 00:00:00 31-MAY-2015 00:00:00   75.000000
0013         01-JUN-2015 00:00:00 31-DEC-2100 00:00:00

是否可以使用一个SQL查询(如果可能,我不想使用sql过程)?有什么建议吗?

1 个答案:

答案 0 :(得分:2)

您正尝试将相邻的行组合在一起,基于日期和PERC相同。我们的想法是使用left join来确定哪些值开始新的范围。然后,使用累计和来计算每行的启动次数。后一个值可用于分组。

在SQL Server 2012+中,累积总和可以直接完成。在早期版本中,您将使用outer apply

生成的查询如下所示:

select k_pkey, min(d_from) as d_from, max(d_to) as d_to, perc
from (select t.*,
             sum(IsGroupStart) over (partition by k_pkey, perc order by d_from) as grp
      from (select t.*,
                   (case when t_prev.k_pkey is null then 1 else 0 end) as IsGroupStart
            from t left join
                 t tprev
                 on tprev.k_pkey = t.k_pkey and
                    (tprev.perc = t.perc or tprev.perc is null and t.perc is null) and
                    tprev.d_to = dateadd(day, -1, t.d_from)
           ) t
      ) t
group by grp, k_pkey, perc;