T-SQL 带聚合的多层 CTE 查询

T-SQL Multi-layered CTE query with Aggregates

我有一个很长的通用 Table 表达式 (CTE) 查询,它试图计算每个用户的平均分数和组平均分数之间的百分比差异。

我希望我的多层 CTE 查询能够过滤大量记录并将其减少到以下 table:

UserID     Tag     UserAvg     GroupAvg     PercentDifference
1          Cat     72.50       73           -0.68
2          Cat     75.50       73           3.36
3          Cat     75          73           2.70
4          Cat     73.25       73           0.34
5          Cat     52.3333     73           -32.97
6          Cat     86.25       73           16.64

我的问题是获取 GroupAvg 列以便我可以执行 % Difference 计算。

说明我目前使用的方法;这是我的 CTE 查询的摘要:

WITH
-- select 1st 3 columns
UserScores    AS (select UserID, Tag, Score FROM {multiple-table} WHERE Tag = 'Cat'),
-- add UserAvg column by grouping records
ScoreAverages AS (select UserID, Tag, AVG(Score) AS UserAvg GROUP BY UserID, Tag FROM UserScores),
-- calculate GroupAvg
GroupAverage  AS (select AVG(UserAvg) AS GroupAvg FROM ScoreAverages),
-- calculate % difference
PercentDiff   AS (select UserID, Tag, UserAvg, 73 AS GroupAvg, (((UserAvg-73)/((UserAvg+73)/2))*100) AS PercentDifference FROM ScoreAverages )
-- do something with results
select * from PercentDiff

足够简单;对吗?

请注意,我已将 73 硬编码为我的 GroupAvg 值。我不确定如何构建允许我从 ScoreAveragesPercentDiff table.

所需的 sql 查询

是否可以在 SELECT 语句中执行 SELECT?我不是在寻找以下内容:

select * from X where Id in (select Id from Y where Name like '%abc%')

或者我只是想一次做太多?

是的,这叫做子select:

SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage, Column3
FROM ...

要在另一列的计算中使用 sub-select 的结果,您可以重复 sub-select:

SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage, (Column3 - (SELECT QUERY THAT GETS GROUP AVERAGE)) AS Column4
FROM ...

或者您可以像引用外部查询或后续 CTE 中的任何其他列一样引用它:

WITH CTE1 AS (SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage
FROM ...)
  , CTE2 AS (SELECT *, Column3-GroupAverage) AS Column4
  FROM CTE1
  JOIN ...

这是可能的,如 Tab Alleman 的回答所示,但在您的情况下则没有必要。由于您已经计算了 cte 链中的 GroupAvg,因此您可以在最终查询中使用它。由于 GroupAverage 只包含一行,您可以简单地向其添加 CROSS JOIN:

;WITH
-- select 1st 3 columns
UserScores    AS (
     select UserID, Tag, Score 
     FROM {multiple-table} 
     WHERE Tag = 'Cat'),
-- add UserAvg column by grouping records
ScoreAverages AS (
     select UserID, Tag, AVG(Score) AS UserAvg 
     FROM UserScores
     GROUP BY UserID, Tag),
-- calculate GroupAvg
GroupAverage  AS (
     select AVG(UserAvg) AS GroupAvg 
     FROM ScoreAverages),
-- calculate % difference
PercentDiff   AS (
     select UserID, Tag, UserAvg, GroupAvg, 
            (((UserAvg-GroupAvg)/((UserAvg+GroupAvg)/2))*100) AS PercentDifference 
     FROM ScoreAverages 
     CROSS JOIN GroupAverage)
-- do something with results
select * from PercentDiff

我只是认为你可以像这样用一个 cte 来做到这一点。

;WITH UserAverages AS
(
    SELECT  UserID,
            Tag,
            AVG(Score) AS UserAvg,
            AVG(AVG(Score)) OVER () AS GroupAvg
    FROM    {multiple-table}
    WHERE   Tag = 'Cat'
    GROUP BY UserID, Tag
)
SELECT  UserID,
        Tag,
        UserAvg,
        GroupAvg,
        (((UserAvg-GroupAvg)/((UserAvg+GroupAvg)/2))*100) AS PercentDifference
FROM    UserAverages