【问题标题】:Selecting SUM of TOP 2 values within a table with multiple GROUP in SQL在 SQL 中选择具有多个 GROUP 的表中的 TOP 2 值的总和
【发布时间】:2011-04-18 22:41:37
【问题描述】:

我一直在使用 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. 获取最长休息时间,按 HoursCTR 分组
  2. 选择返回每个 HoursCTR 的最大持续时间的第一个(最小)RestCTR 行
  3. 重复第 1 步(不包括第 2 步中已收集的行)
  4. 重复第 2 步(同样,排除第 2 步中收集的行)
  5. 将 RestCTR 行(来自第 2 步和第 4 步)合并到单个表中
  6. 获取第 5 步中的行所指向的持续时间的总和,按 HoursCTR 分组

如果有任何设置功能可以减少此过程,他们将非常受欢迎。

【问题讨论】:

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


    【解决方案1】:

    在 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 子句,以获得两个最长的休息。


    过失,我没有注意到您需要它才能在 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
    

    【讨论】:

    • 它如何选择两个 最长 间断?我错过了什么?
    • OP 指示 SQL Server 2000。ROW_NUMBER() 和 CTE 不可用。
    • @bobs:谢谢,我错过了。我添加了不同的解决方案。
    • 谢谢比尔,因为这是一个嵌套(相关)子查询,我一直试图避免它,但如果它不是太受欢迎,我会考虑它。话虽如此,我根本无法接受您的建议——请您看一下语法,看看 SQL Server 2000 需要做什么才能接受此产品?
    • 可能是因为我忘了给最里面的子查询一个表别名。我会编辑那个。抱歉,我没有要测试的 MS SQL Server 2000 副本。
    【解决方案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
    

    【讨论】:

      【解决方案3】:

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

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

      我认为,#Periods 表的敲击次数可能与您在该表中的单个行一样多。

      另外,在我看来,相关子查询有些问题。由于我像躲避瘟疫一样避开它们,知道它们是邪恶的,所以我不知道如何解决它。

      【讨论】:

      • 谢谢大卫,我希望有人知道我还没有发现的巧妙套路,但看起来这个 6 步过程可能已经结束了。
      • 嗯......你告诉数据库引擎该做什么以及如何去做,这并不是一件坏事,真的。归根结底,引擎并不是那么明亮。如果你告诉它这样做,它怎么知道你不希望它迭代垃圾?
      猜你喜欢
      • 2014-08-15
      • 2022-12-08
      • 1970-01-01
      • 2018-11-16
      • 2019-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      相关资源
      最近更新 更多