在SQL中选择具有多个GROUP的表中的TOP 2值的SUM

时间:2010-09-29 16:51:44

标签: sql sql-server set sql-server-2000

我一直在使用SQL Server 2000中的集合,并为我的一个临时表(#Periods)提供以下表格结构:

    RestCTR     HoursCTR    Duration    Rest
    ----------------------------------------
    1           337         2           0
    2           337         46          1
    3           337         2           0
    4           337         46          1
    5           338         1           0
    6           338         46          1
    7           338         2           0
    8           338         46          1
    9           338         1           0
    10          339         46          1
    ...

我想要做的是计算每个HoursCTR的2个最长休息时间的总和,最好使用集合和临时表(而不是游标或嵌套子查询)。

这是梦想的查询,它在SQL中无效(无论我运行多少次):

Select HoursCTR, SUM ( TOP 2 Duration ) as LongestBreaks
FROM #Periods
WHERE Rest = 1
Group By HoursCTR    

HoursCTR可以有任意数量的休息期(包括没有)。

我目前的解决方案不是很优雅,基本上涉及以下步骤:

  1. 获取最长休息时间,按小时数分组
  2. 选择返回每个HoursCTR
  3. 的最长持续时间的第一个(最小)RestCTR行
  4. 重复步骤1(不包括步骤2中已收集的行)
  5. 重复步骤2(再次,排除在步骤2中收集的行)
  6. 将RestCTR行(从步骤2和4)合并到单个表
  7. 获取步骤5中行所指示的持续时间的和,按HoursCTR
  8. 分组

    如果有任何设定的功能可以减少这个过程,那么非常受欢迎。

3 个答案:

答案 0 :(得分:7)

在SQL Server中执行此操作的最佳方法是使用common table expression,使用窗口函数ROW_NUMBER()对每个组中的行进行编号:

WITH NumberedPeriods AS (
  SELECT HoursCTR, Duration, ROW_NUMBER() 
    OVER (PARTITION BY HoursCTR ORDER BY Duration DESC) AS RN
  FROM #Periods
  WHERE Rest = 1
)
SELECT HoursCTR, SUM(Duration) AS LongestBreaks
FROM NumberedPeriods
WHERE RN <= 2
GROUP BY HoursCTR

编辑:我在分区中添加了一个ORDER BY子句,以获得两个最长的休止符。


Mea culpa,我没注意到你需要在Microsoft SQL Server 2000中使用它。该版本不支持CTE或窗口功能。我会留下上面的答案,以防它帮助其他人。

在SQL Server 2000中,常见的建议是使用相关的子查询:

SELECT p1.HoursCTR, (SELECT SUM(t.Duration) FROM 
    (SELECT TOP 2 p2.Duration FROM #Periods AS p2
     WHERE p2.HoursCTR = p1.HoursCTR 
     ORDER BY p2.Duration DESC) AS t) AS LongestBreaks
FROM #Periods AS p1

答案 1 :(得分:2)

SQL 2000没有CTE,也没有ROW_NUMBER() 使用group by时,相关子查询可能需要额外的步骤。

这应该适合你:

SELECT 
    F.HoursCTR,
    MAX (F.LongestBreaks) AS LongestBreaks -- Dummy max() so that groupby can be used.
FROM
    (
        SELECT 
            Pm.HoursCTR, 
            (
                SELECT 
                    COALESCE (SUM (S.Duration), 0)    
                FROM 
                    (
                        SELECT TOP 2    T.Duration
                        FROM            #Periods    AS T
                        WHERE           T.HoursCTR  = Pm.HoursCTR 
                        AND             T.Rest      = 1
                        ORDER BY        T.Duration  DESC
                    ) AS S
             ) AS LongestBreaks
        FROM
            #Periods AS Pm
    ) AS F
GROUP BY
    F.HoursCTR

答案 2 :(得分:1)

不幸的是,Alex,你得到了正确的解决方案:相关的子查询,取决于它们的结构,将最终多次触发,可能会给你数百个单独的查询执行。

将您当前的解决方案放入查询分析器,启用“显示执行计划”(Ctrl + K),然后运行它。您将在底部有一个额外的标签,它会告诉您引擎如何收集结果。如果对相关子查询执行相同操作,则会看到该选项的作用。

我相信它可能会使#Periods表的次数与该表中的各行相同。

另外 - 关于相关子查询的一些事情在我看来。既然我像瘟疫一样避开它们,知道它们是邪恶的,我不知道如何解决这个问题。