Tsql 有条件地合并和聚合T-SQL中的相邻行
我有一个10万行的表格,代表特定时间段内的销售额。通常经期至少有几个小时,但偶尔我们也会有几分钟的经期。这些微小的周期扰乱了下游报告,所以我想将它们与前一个周期合并。任何30分钟或更短的时间段都应与上一个时间段合并,并在不同时间段之间汇总销售数据。在长周期之间可能有零个、一个或多个后续短周期。数据中没有时间间隔-一个周期的开始始终与前一个周期的结束相同 这是一种很好的基于集合的方式,没有光标!要执行此合并吗 现有简化数据如下所示:Tsql 有条件地合并和聚合T-SQL中的相邻行,tsql,sql-server-2008-r2,pivot,set-based,Tsql,Sql Server 2008 R2,Pivot,Set Based,我有一个10万行的表格,代表特定时间段内的销售额。通常经期至少有几个小时,但偶尔我们也会有几分钟的经期。这些微小的周期扰乱了下游报告,所以我想将它们与前一个周期合并。任何30分钟或更短的时间段都应与上一个时间段合并,并在不同时间段之间汇总销售数据。在长周期之间可能有零个、一个或多个后续短周期。数据中没有时间间隔-一个周期的开始始终与前一个周期的结束相同 这是一种很好的基于集合的方式,没有光标!要执行此合并吗 现有简化数据如下所示: UnitsSold Start
UnitsSold Start End
---------------------------------------------------
10 06-12-2013 08:03 06-12-2013 12:07
12 06-12-2013 12:07 06-12-2013 16:05
1 06-12-2013 16:05 06-12-2013 16:09
1 06-12-2013 16:09 06-12-2013 16:13
7 06-12-2013 16:13 06-12-2013 20:10
UnitsSold Start End
---------------------------------------------------
10 06-12-2013 08:03 06-12-2013 12:07
14 06-12-2013 12:07 06-12-2013 16:13
7 06-12-2013 16:13 06-12-2013 20:10
所需的输出如下所示:
UnitsSold Start End
---------------------------------------------------
10 06-12-2013 08:03 06-12-2013 12:07
12 06-12-2013 12:07 06-12-2013 16:05
1 06-12-2013 16:05 06-12-2013 16:09
1 06-12-2013 16:09 06-12-2013 16:13
7 06-12-2013 16:13 06-12-2013 20:10
UnitsSold Start End
---------------------------------------------------
10 06-12-2013 08:03 06-12-2013 12:07
14 06-12-2013 12:07 06-12-2013 16:13
7 06-12-2013 16:13 06-12-2013 20:10
不幸的是,我们仍然使用SQLServer2008R2,因此我们无法利用SQLServer2012中很酷的新窗口函数,这可能会使这个问题更容易有效地解决
关于类似的问题,有一个很好的讨论。我特别喜欢PIVOT/UNPIVOT解决方案,但我很难适应我的问题 我的想法是
仅创建具有长句点的列表
使用外部应用查找下一个长周期的开始
带子查询的求和单位
像这样的
declare @t table (UnitsSold int, start datetime, finish datetime)
insert into @t values (10, '20130612 08:03', '20130612 12:07')
insert into @t values (12, '20130612 12:07', '20130612 16:05')
insert into @t values (1, '20130612 16:05', '20130612 16:09')
insert into @t values (1, '20130612 16:09', '20130612 16:13')
insert into @t values (7, '20130612 16:13', '20130612 20:10')
select
(select SUM(UnitsSold) from @t t3 where t3.start>=t1.start and t3.finish<=ISNULL(oa.start, t1.finish)) as UnitsSold,
t1.start,
ISNULL(oa.start, t1.finish) as finish
from @t t1
outer apply (
select top(1) start
from @t t2
where datediff(minute,t2.start, t2.finish)>30
and t2.start >= t1.finish
order by t2.start
) oa
where datediff(minute, t1.start, t1.finish)>30
使用递归CTE:
DECLARE @t TABLE (UnitsSold INT, Start DATETIME, Finish DATETIME)
INSERT INTO @t VALUES
(10, '06-12-2013 08:03', '06-12-2013 12:07'),
(12, '06-12-2013 12:07', '06-12-2013 16:05'),
(1, '06-12-2013 16:05', '06-12-2013 16:09'),
(1, '06-12-2013 16:09', '06-12-2013 16:13'),
(7, '06-12-2013 16:13', '06-12-2013 20:10')
;WITH rec AS (
-- Returns periods > 30 minutes
SELECT u.UnitsSold, u.Start, u.Finish
FROM @t u WHERE DATEDIFF(MINUTE, u.Start, u.Finish) > 30
UNION ALL
-- Adds on adjoining periods <= 30 minutes
SELECT
u.UnitsSold + r.UnitsSold,
r.Start,
u.Finish
FROM rec r
INNER JOIN @t u ON r.Finish = u.Start
AND DATEDIFF(MINUTE, u.Start, u.Finish) <= 30)
-- Since the CTE also returns incomplete periods we need
-- to filter out the relevant periods, in this case the
-- last/max values for each start value.
SELECT
MAX(r.UnitsSold) AS UnitsSold,
r.Start AS Start,
MAX(r.Finish) AS Finish
FROM rec r
GROUP BY r.Start
使用CTE和累计总和:
DECLARE @t TABLE (UnitsSold INT, Start DATETIME, Finish DATETIME)
INSERT INTO @t VALUES
(10, '06-12-2013 08:03', '06-12-2013 12:07'),
(12, '06-12-2013 12:07', '06-12-2013 16:05'),
(1, '06-12-2013 16:05', '06-12-2013 16:09'),
(1, '06-12-2013 16:09', '06-12-2013 16:13'),
(7, '06-12-2013 16:13', '06-12-2013 20:10')
;WITH groups AS (
SELECT UnitsSold, Start, Finish,
-- Cumulative sum, IIF returns 1 for each row that
-- should generate a new row in the final result.
SUM(IIF(DATEDIFF(MINUTE, Start, Finish) <= 30, 0, 1)) OVER (ORDER BY Start) csum
FROM @t)
SELECT
SUM(UnitsSold) UnitsSold,
MIN(Start) Start,
MAX(Finish) Finish
FROM groups
GROUP BY csum