【问题标题】:AVG and COUNT in SQL ServerSQL Server 中的 AVG 和 COUNT
【发布时间】:2011-12-16 11:06:41
【问题描述】:

我有一个评分系统,任何人都可以在其中评价其他人。每个人都可以被一个人多次评判。对于平均值的计算,我想只包括最新的值。

这可以通过 SQL 实现吗?

  • 2011 年 2 月 1 日,第 1 个人用 5 评价第 2 个人
  • 2011 年 3 月 1 日,第 1 个人对第 2 个人和第 2 个人进行评分
  • 2011 年 2 月 1 日,第 2 个人对第 1 个人评价为 6
  • 2011 年 3 月 1 日,第 2 个人对第 1 个人和第 3 个人进行评分
  • 2011 年 5 月 1 日,第 3 个人对第 1 个人和第 5 个人进行评分

结果:

  • 第 2 个人的平均值为 2。
  • 第 1 个人的平均值为 4。

表格可能如下所示:evaluator, evaluatee, rating, date。

亲切的问候

迈克尔

【问题讨论】:

  • 您的表是否也有代理合成主键?也许是一个自动递增的身份整数?如果是这样,它将使查询更容易和更高效。

标签: sql sql-server average


【解决方案1】:

完全有可能。

假设您的表结构如下所示:

CREATE TABLE [dbo].[Ratings](
    [Evaluator] varchar(10),
    [Evaluatee] varchar(10),
    [Rating] int,
    [Date] datetime
);

以及像这样的值:

INSERT INTO Ratings
    SELECT 'Person 1', 'Person 2', 5, '2011-02-01' UNION
    SELECT 'Person 1', 'Person 2', 2, '2011-03-01' UNION
    SELECT 'Person 2', 'Person 1', 6, '2011-02-01' UNION
    SELECT 'Person 2', 'Person 1', 3, '2011-03-01' UNION
    SELECT 'Person 3', 'Person 1', 5, '2011-05-01'

那么第 1 个人的平均评分是:

SELECT AVG(Rating) FROM Ratings r1
    WHERE Evaluatee='Person 1' and not exists
    (SELECT 1 FROM Ratings r2
        WHERE r1.Evaluatee = r2.Evaluatee AND
            r1.evaluator=r2.evaluator AND
            r1.date < r2.date)

结果:

4

或者对于所有被评估者,按被评估者分组:

SELECT Evaluatee, AVG(Rating) FROM Ratings r1
    WHERE not exists
    (SELECT 1 FROM Ratings r2
        WHERE r1.Evaluatee = r2.Evaluatee AND
            r1.evaluator = r2.evaluator AND
            r1.date < r2.date)
    GROUP BY Evaluatee

结果:

Person 1    4
Person 2    2

这可能看起来隐含假设不存在具有相同日期的条目; 但这实际上不是问题:如果可以存在这样的条目,那么无论如何您都无法确定其中哪些是后来创建的;你只能在它们之间随机选择。就像这里显示的那样,它们都被包括在内并被平均了——这可能是你在那个边界情况下可以获得的最好的解决方案(尽管它稍微有利于那个人,给他两票)。

要完全避免这个问题,您可以简单地将 Date 作为主键或唯一索引的一部分 - 这里明显的主键选择是列(Evaluator、Evaluatee、Date)。

【讨论】:

  • +1 - 我非常喜欢这种方法,我会尊重地删除我的答案:)
【解决方案2】:
declare @T table
(
  evaluator int, 
  evaluatee int, 
  rating int,
  ratedate date
)

insert into @T values
(1, 2, 5, '20110102'),
(1, 2, 2, '20110103'),
(2, 1, 6, '20110102'),
(2, 1, 3, '20110103'),
(3, 1, 5, '20110105')

select evaluatee,
       avg(rating) as avgrating 
from (       
      select evaluatee,
             rating,
             row_number() over(partition by evaluatee, evaluator 
                               order  by ratedate desc) as rn
      from @T
     ) as T
where T.rn = 1
group by evaluatee

结果:

evaluatee   avgrating
----------- -----------
1           4
2           2

【讨论】:

    【解决方案3】:

    这是可以做到的,但它可能真的很麻烦 - SQL 的设计目的不是比较行,而是比较列。我强烈建议您保留一个仅包含最新数据的附加表,并将其余数据存储在存档表中。

    如果您必须这样做,那么我需要一个完整的表结构来尝试为此编写查询。特别是我需要知道哪些是唯一索引。

    【讨论】:

      猜你喜欢
      • 2021-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多