MS SQL:按日期分组

时间:2014-06-14 13:39:28

标签: sql-server

ID      DateTime    EmailCount
93  6/1/2014 00:00:00   4
94  6/2/2014 00:00:00   4
95  6/3/2014 00:00:00   2
96  6/4/2014 00:00:00   2
97  6/5/2014 00:00:00   2
98  6/6/2014 00:00:00   2
99  6/7/2014 00:00:00   2
73  6/8/2014 00:00:00   2
74  6/9/2014 00:00:00   2
75  6/10/2014 00:00:00  4
76  6/11/2014 00:00:00  4
77  6/12/2014 00:00:00  2
78  6/13/2014 00:00:00  2
79  6/14/2014 00:00:00  2
80  6/16/2014 00:00:00  2
81  6/17/2014 00:00:00  4
82  6/18/2014 00:00:00  4
83  6/19/2014 00:00:00  4
84  6/20/2014 00:00:00  4
100 6/21/2014 00:00:00  4
101 6/22/2014 00:00:00  4
102 6/23/2014 00:00:00  4
103 6/24/2014 00:00:00  4
89  6/27/2014 00:00:00  4
90  6/28/2014 00:00:00  4
91  6/29/2014 00:00:00  4
92  6/30/2014 00:00:00  4
104 7/1/2014 00:00:00   4
105 7/2/2014 00:00:00   4
106 7/3/2014 00:00:00   4
121 7/6/2014 00:00:00   2
122 7/7/2014 00:00:00   2
123 7/8/2014 00:00:00   2

生成输出

Startdate                EndDate            EmailCount
6/3/2014 00:00:00   6/14/2014 00:00:00  2
6/16/2014 00:00:00  6/16/2014 00:00:00  2
7/6/2014 00:00:00   7/8/2014 00:00:00   2
6/1/2014 00:00:00   6/11/2014 00:00:00  4
6/17/2014 00:00:00  6/24/2014 00:00:00  4
6/27/2014 00:00:00  7/3/2014 00:00:00   4

这里,生成的输出并不完美,因为我想在以下组中使用StartDate到EndDate:(2014年6月3日到6/9/2014和EmailCount = 2)和(2014年6月10日到6月11日) 2014年和EmailCount = 4)和(2014年6月12日至2014年6月14日和EmailCount = 2)。此外,不应将数据库中的日期添加到组中。

2 个答案:

答案 0 :(得分:0)

一个有点复杂的查询来解释,但这是一个尝试;

如果时间总是午夜,则可以使用公用表表达式为每行分配行号,并按日期和行号之间的差异进行分组。只要序列没有被破坏(即日期是连续的并且具有相同的emailid),它们将最终在同一组中,外部查询可以轻松地提取每个组的开始和结束日期;

WITH cte AS (
 SELECT dateandtime, emailid,
   ROW_NUMBER() OVER (PARTITION BY emailid ORDER BY dateandtime) rn
 FROM mytable
)
SELECT MIN(dateandtime) start_time, 
       MAX(dateandtime) end_time, 
       MAX(emailid) emailid
FROM cte GROUP BY DATEADD(d, -rn, dateandtime) ORDER BY start_time

An SQLfiddle to test with

如果日期时间不总是午夜,则分组将失败。如果是这种情况,您可以添加一个公用表表达式,在运行此查询之前将日期时间转换为单独的步骤。

答案 1 :(得分:0)

您正在寻找具有相同EmailID的块中连续日期的运行。这假设您在日期中没有间隙。我不确定它是最优雅的方法,但你可以在这个主题上找到很多东西。

with BlockStart as (
    select t.StartDate, t.EmailID
    from T as t left outer join T as t2
        on t2.StartDate = t1.StartDate - 1 and t2.EmailID = t1.EmailID
    where t2.StartDate is null
    union all
    select max(StartDate) + 1, null
    from T
) as BlockStart
select
    StartDate,
    (select min(StartDate) - 1 from BlockStart as bs2 where bs2 > bs.StartDate) as EndDate,
    EmailID
from BlockStart as bs
where
    EmailID is not null
    -- /* or */ exists (select 1 from BlockStart as bs3 where bs3.StartDate > bs.StartDate)