如何在窗口函数中计算时截断值

问题描述 投票:0回答:1

我希望我的标题能在一定程度上反映这个问题......

我有表格课程,我每年跟踪学生获得的分数。现在我必须运行一个统计点的查询。但是:任何超过 150 或 150 倍数的行,该学生在那一年都会失去高于 TotaPoints/150 的分数。

这是 SQL 小提琴:http://sqlfiddle.com/#!18/e84bf/10

这是简单的模式,我在这里手动输入 CorrectResult 进行测试:

CREATE TABLE Courses
    ([Year] int, [Points] int, [CorrectResult] int)
;
    
INSERT INTO Courses
    ([Year], [Points], [CorrectResult])
VALUES
    (1, 148, 148),
    (2, 4, 150),
    (3, 149, 299)
;

这是我同时错误的查询:

SELECT 
  Year,
  Points,
  CorrectResult,
  TotalCredits - LostPointsTally as RelevantPoints 
FROM (
  SELECT 
    Year,
    Points,
    CorrectResult,
    TotalCredits,
    LAG(LevelGroup, 1, 0) OVER (ORDER BY Year) AS PreviousLevelGroup,
    CASE 
      WHEN LevelGroup > LAG(LevelGroup, 1, 0) OVER (ORDER BY Year) THEN 
        TotalCredits - (LAG(LevelGroup, 1, 0) OVER (ORDER BY Year) * 150)
      ELSE 
        0
    END AS LostPointsTally
  FROM (
    SELECT 
      Year,
      Points,
      CorrectResult,
      TotalCredits,
      FLOOR(TotalCredits / 150) + 1 AS LevelGroup
    FROM (
      SELECT 
        Year,
        Points,
        CorrectResult,
        SUM(CASE 
          WHEN Points > 150 THEN 150
          ELSE Points
        END) OVER (ORDER BY Year) AS TotalCredits
      FROM Courses
    ) t
  ) t1
) t2;

运行此查询,在第 3 行您得到 300,这是错误的。我们需要得到 150+149,即 299。

(我希望我能说清楚,如果没有,我将非常乐意进一步澄清)

请指教

非常感谢

sql-server window-functions
1个回答
0
投票

据我了解你的问题,你想要计算一个调整后的运行总计,只要超过 150 的倍数,总计就会被限制在 150 的倍数。后面的行的运行总计从该点开始继续,直到达到下一个 150 倍数。此外,每条记录的分数限制为 150,因此同一年内不可能超过 150 的倍数。

因为每行的计算取决于前一行的重要调整计算,所以我认为这不能使用带有运行总计和 LAG() 函数的简单聚合来完成。这将需要迭代计算,可能使用递归 CTE(公用表表达式)。

以下使用多个 CTE 来构建结果。第一个 CTE 对每年可以贡献的积分进行了限制。第二个 CTE 是递归 CTE,其中以第一年为基础,并递归地包括与前一年结果相结合的后续年份。

我通过计算限制当年运行总和的 LevelUpThreshold 值重新设计了您的

LevelGroup
逻辑。然后计算先前调整和+当前调整点并与计算出的
LevelUpThreshold
进行比较以选择新的调整和。
LEAST()
函数对此非常有效。

生成的查询类似于:

WITH CTE_Adjusted_Points AS (
    SELECT C.*, LEAST(Points, 150) AS AdjustedPoints
    FROM Courses C
),
CTE_Adjusted_Sum AS (
    SELECT
        P.*,
        P.AdjustedPoints AS AdjustedSum
    FROM CTE_Adjusted_Points P
    WHERE P.Year = 1
    UNION ALL
    SELECT
        P.*,
        LEAST(S.AdjustedSum + P.AdjustedPoints, L.LevelUpThreshold) AS AdjustedSum
    FROM CTE_Adjusted_Sum S
    JOIN CTE_Adjusted_Points P ON P.Year = S.year + 1
    CROSS APPLY (
        SELECT (S.AdjustedSum / 150 + 1) * 150 AS LevelUpThreshold
    ) L
)
SELECT S.Year, S.Points, S.AdjustedPoints, S.AdjustedSum, S.CorrectResult
FROM CTE_Adjusted_Sum S
ORDER BY S.Year;

结果(带有一些额外的测试数据):

年份 积分 调整积分 调整后的总和 正确结果
1 148 148 148 148
2 4 4 150 150
3 149 149 299 299
4 10 10 300 300
5 500 150 450 450
6 70 70 520 520
7 70 70 590 590
8 70 70 600 600
9 70 70 670 670

请参阅 this db<>fiddle 以获取工作示例。

© www.soinside.com 2019 - 2024. All rights reserved.