聚合查询-查询列影响聚合

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

我有一个表“ Scores”,其字段如下:

UserId
LessonId
ExerciseId
Score
Timestamp

我想设置一个视图“ vw_AggregateScoreForUser”,该视图将聚合该表中的数据,如下所示:

SELECT UserId, 
       LessonId, 
       COUNT(ExerciseId) AS TotalExercises,
       SUM(Score) AS TotalScore, 
       COUNT(DISTINCT CONVERT(date, Timestamp)) AS StudyDays
FROM Scores
GROUP BY UserId, LessonId

最棘手的是StudyDays,我要在其中计算用户至少在此处输入一个条目的唯一日期-这是我“学习”即完成至少一项练习的日期。

现在,说我要在第1到第5课中执行该视图。

SELECT FROM vw_AggregateScoreForUser WHERE UserId = 1 AND LessonId BETWEEN 1 AND 5;

我想要的是返回的一条记录,该记录汇总了这5课的数据。但是通过上述设置,数据按LessonId分组,因此我将获得5条记录。

问题是,由于每课计算的StudyDays现在可能不正确。例如。包含以下数据:

UserId    LessonId    ExerciseId    ...     Timestamp
1         1           1                     2019-11-21 09:00
1         1           2                     2019-11-22 10:00
1         2           1                     2019-11-22 11:00

我会得到结果

UserId    LessonId    TotalExercises ...    StudyDays
1         1           2                     2
1         2           1                     1

我不能简单地添加StudyDays来获得学习的天数。那将给我3,但整个StudyDays的独特计数应为2。

问题是我需要在视图中使用LessonId以便能够在WHERE子句中使用它,但是在视图中使用它会按照课程对我的数据进行分组,从而导致聚合不正确。

如何在视图中包括一个字段,以便可以对其进行过滤,而又不影响该视图中发生的聚合?

sql-server tsql sql-view
1个回答
2
投票
某些分组汇总不能堆叠成多个级别,因为它们会产生不同的结果。计数差异与计数差异与应用原始集合中的计数差异不同。考虑到行数的平均值也会发生同样的情况。

您遇到的问题是视图内部带有GROUP BY LessonIDCOUNT DISTINCT。想要(以后)将多个LessonID值作为一个集合一起计算时,您已经在按LessonID计算值。

只要您将GROUP BY保留在视图内,就会出现此问题。一种解决方案是更改表值函数的视图,该视图允许提供一系列课程:

CREATE FUNCTION dbo.ufnUserLessonSummary ( @UserID INT, @LessonIDFrom INT, @LessonIDTo INT) RETURNS TABLE AS RETURN SELECT UserId, LessonId, COUNT(ExerciseId) AS TotalExercises, SUM(Score) AS TotalScore, COUNT(DISTINCT CONVERT(date, Timestamp)) AS StudyDays FROM Scores AS S WHERE S.UserID = @UserID AND S.LessonID BETWEEN @LessonIDFrom AND @LessonIDTo GROUP BY UserId, LessonId

您可以按以下方式查询它:

SELECT S.* FROM dbo.ufnUserLessonSummary(1, 1, 5) AS S

但是,这仅限于一系列课程。如果只需要课程135,该怎么办?另一个更复杂但更通用的选项是将SP与预加载的输入表一起使用:

CREATE PROCEDURE dbo.uspUserLessonSummary AS BEGIN SELECT UserId, LessonId, COUNT(ExerciseId) AS TotalExercises, SUM(Score) AS TotalScore, COUNT(DISTINCT CONVERT(date, Timestamp)) AS StudyDays FROM Scores AS S INNER JOIN #UserLesson AS U ON S.UserID = U.UserID AND S.LessonID = U.LessonID GROUP BY UserId, LessonId END

您可以在执行之前通过加载临时表来提供所需的记录:

IF OBJECT_ID('tempdb..#UserLesson') IS NOT NULL DROP TABLE #UserLesson CREATE TABLE #UserLesson ( UserID INT, LessonID INT) INSERT INTO #UserLesson ( UserID, LessonID) VALUES (1, 1), (1, 2), (1, 3), (1, 4), (1, 5) EXEC dbo.uspUserLessonSummary

您还可以通过这种方法使用变量表。
© www.soinside.com 2019 - 2024. All rights reserved.