MSSQL:考虑日期范围的 DISTINCT
MSSQL: DISTINCT that takes date-ranges into consideration
我有一个 table 看起来不太像:
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 过程)?有什么建议吗?
您正试图根据日期和 PERC
相同的日期将相邻的行合并在一起。这个想法是使用 left join
来确定哪些值开始一个新的范围。然后,使用累计和来计算每行的启动次数。后一个值可用于分组。
在SQLServer 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;
我有一个 table 看起来不太像:
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 过程)?有什么建议吗?
您正试图根据日期和 PERC
相同的日期将相邻的行合并在一起。这个想法是使用 left join
来确定哪些值开始一个新的范围。然后,使用累计和来计算每行的启动次数。后一个值可用于分组。
在SQLServer 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;