2011-12-10 19 views
2

我有一张表,其中包含一个人员列表和一个持续时间值,我需要将表中每个人的总持续时间相加,并显示每个人的记录数。不使用游标的SQL条件分组

PersonTable 
id int 
name nvarchar(max) 
duration decimal(18,2) 

SELECT name, sum(duration) as TotalDuration, count(*) as NumRecords 
FROM PersonTable 
GROUP BY name 

不幸的是,我还需要确保没有人被放置在持续时间超过3小时的组中。如果某个人的记录总数大于3小时,那么我需要为该人生成2个组,或者根据需要为总数保留3个以下的记录。基本表中没有单个记录包含持续时间> 3,所以一组有效的组总是可能的。

为了清楚测试数据:

id, person,  duration 
1, John Smith, 2hrs 
2, John Smith, 1hrs 
3, John Smith, 1hrs 
4, John Smith, 2hrs 
1, Jane Doe, 1hrs 
1, Jane Doe, 1hrs 
1, Jane Doe, 2hrs 
8, Jack Foo, 1hrs 

输出:(序列号来识别同一个人的不同的组)

name, Total Duration, Num Records, Sequence Number 
John Smith, 3hrs,  2,   1 
John Smith, 3hrs,  2,   2 
Jane Doe, 2hrs,  2,   1 
Jane Doe, 2hrs,  1,   2 
Jack Foo, 1hrs,  1,   1 

这issustrates的基本问题。在实践中有更多的字段可以分组,并且我还需要枚举基本记录的ID,因为这些记录将在稍后的阶段中使用。

我目前的解决方案是只使用游标遍历排序后的表,并在累计总小时数超过3时将新组输出到临时表。temorpary表还包含一列逗号分隔的每行id值的基表有助于分组。

但是我想知道是否有更好的(没有游标)解决这类问题。
也可以将聚合连接回基表,所以我有一张每个记录的表,再加上一个序列号,允许我将人和序列号分组以重现上面的表格?这将是一个更好的解决方案,比生成逗号分隔的ID列表,用于在过程的后期阶段找到原始记录。

+0

该表是否具有将用于排序和分组的“主键”或其他列? –

+0

是的,一个简单的自动增量ID列 – Trent

回答

1

恐怕没有办法用标准SQL做你想做的事。问题如下:如果您通过某些属性对行进行分组,这将始终将每个行组缩减为一行;如果你没有分组,那么你将有原来的行数。没有办法将一组三行减少到两行。

将基表连接到名称列上的分组聚合不会有帮助,因为您再次获得与基表一样多的行。过滤此连接的结果也无济于事,因为连接会将相同的信息添加到具有相同名称的每一行,因此不允许您在连接之前区分它们。

是否也有可能将聚合连接回基表,因此我有一张每个记录的表格以及一个序列号,允许我对人员和序列号进行分组以重现上述表格?

如果您的序列号在每个名称组中都是唯一的,则根据名称和序列号进行分组完全相当于根本不分组。所以你需要非唯一的序列号,即一些具有相同名字的行需要得到相同的序列号;但是杀手是为了分配序列号来重现结果,序列号必须被分配,以便具有相同名称和序列号的行从来没有持续时间> 3的持续时间。但是如果你能做到这一点,你可以使用那已经解决了这个问题!

我真的认为你的光标解决方案是最合理的做法。纯SQL可能无法解决此问题,因为无法将一组行降至多行。

+0

所以没有解决方案?那回答了这个问题。 – Trent