不使用游标的SQL条件分组

时间:2011-12-10 07:46:58

标签: sql sql-server sql-server-2005

我有一个包含人员列表和持续时间值的表格,我需要将表格中每个人的总持续时间相加,并显示每个人的记录数量。

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

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

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

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

1 个答案:

答案 0 :(得分:1)

我担心没有办法用标准SQL做你想做的事。问题如下:如果按特定属性对行进行分组,则会始终将每个行组减少到一行;如果您不进行分组,那么您将拥有与原始行一样多的行。没有办法将一组三行减少到两行。

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

  

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

如果您的序列号在每个名称组中都是唯一的,则按名称和序列号分组将等同于不进行分组。因此,您需要非唯一的序列号,即一些具有相同名称的行需要获得相同的序列号;但是杀手是为了重现结果的序列号的分配,必须分配序列号,使得具有相同名称和序列号的行永远不具有其总和> 1的持续时间。但是,如果你能做到这一点,你可以用它来解决问题!

我真的认为你的光标解决方案是最合理的事情。纯SQL可能无法解决这个问题,因为它无法将一组行减少到多行。