聚合查询——查询的列影响聚合
Aggregate query - column for querying affects aggregation
我有一个 table "Scores" 字段如下:
UserId
LessonId
ExerciseId
Score
Timestamp
我想设置一个视图 "vw_AggregateScoreForUser" 来聚合来自 table 的数据,如下所示:
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,我在这里计算用户至少有一个条目的唯一日期 - 这给了我他们 "studied" 的天数,即完成至少一项练习。
现在,假设我要为第 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 子句中使用它,但是将它放在视图中会按课程对我的数据进行分组,从而导致聚合不正确。
如何在视图中包含一个字段以便您可以对其进行过滤,而不影响该视图中发生的聚合?
一些分组聚合不能堆叠在多个级别,因为它们会给出不同的结果。与不同计数不同的计数与应用与原始集不同的计数不同。考虑到行数的平均值也会发生同样的情况。
您遇到的问题是 GROUP BY LessonID
和视图内的 COUNT DISTINCT
。当您希望(稍后)将多个 LessonID
值作为一个集合一起计算时,您已经在按 LessonID
计算值。
只要您将 GROUP BY
保持在视图内,您就会遇到这个问题。一种解决方案是更改 table 值函数的视图,这样可以提供一系列课程:
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 table:
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
您可以通过在执行之前加载临时 table 来提供您想要的记录:
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
您也可以通过这种方法使用变量 tables。
我有一个 table "Scores" 字段如下:
UserId
LessonId
ExerciseId
Score
Timestamp
我想设置一个视图 "vw_AggregateScoreForUser" 来聚合来自 table 的数据,如下所示:
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,我在这里计算用户至少有一个条目的唯一日期 - 这给了我他们 "studied" 的天数,即完成至少一项练习。
现在,假设我要为第 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 子句中使用它,但是将它放在视图中会按课程对我的数据进行分组,从而导致聚合不正确。
如何在视图中包含一个字段以便您可以对其进行过滤,而不影响该视图中发生的聚合?
一些分组聚合不能堆叠在多个级别,因为它们会给出不同的结果。与不同计数不同的计数与应用与原始集不同的计数不同。考虑到行数的平均值也会发生同样的情况。
您遇到的问题是 GROUP BY LessonID
和视图内的 COUNT DISTINCT
。当您希望(稍后)将多个 LessonID
值作为一个集合一起计算时,您已经在按 LessonID
计算值。
只要您将 GROUP BY
保持在视图内,您就会遇到这个问题。一种解决方案是更改 table 值函数的视图,这样可以提供一系列课程:
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 table:
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
您可以通过在执行之前加载临时 table 来提供您想要的记录:
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
您也可以通过这种方法使用变量 tables。