我有一个“得分”表,其字段如下:
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子句中使用它,但是在视图中使用它会按照课程对我的数据进行分组,从而导致聚合不正确。
如何在视图中包括一个字段,以便可以对其进行过滤,而又不影响该视图中发生的聚合?
答案 0 :(得分:2)
某些分组聚合不能堆叠成多个级别,因为它们会产生不同的结果。计数差异与计数差异与应用原始集合中的计数差异不同。考虑到行数的平均值也会发生同样的情况。
您遇到的问题是视图中的GROUP BY LessonID
中带有COUNT 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
但是,这仅限于一系列课程。如果只需要课程1
,3
和5
,会发生什么情况?另一个更复杂但更通用的选项是将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
您还可以通过这种方法使用变量表。