2011-12-16 15 views
4

У меня есть рейтинговая система, в которой любой человек может просматривать другие. Каждый человек может быть оценен одним человеком более одного раза. Для вычисления средних я хотел бы включить только самые текущие значения.AVG и COUNT в SQL Server

Возможно ли это с помощью SQL?

  • Person 1 Person 2 ставки с 5 по 1.2.2011 < - игнорируется, потому что есть более новая оценка человека 1
  • Человек 1 Человек 2 ставки с 2 на 1.3.2011
  • Person 2 ставки Person 1 с 6 по 1.2.2011 < - игнорируются, а
  • Люди 2 курса Люди 1 с 3 по 1.3.2011
  • Людей 3 ставок Человек 1 с 5 по 1.5.2011

Результат:

  • Усредненный для лица 2 равен 2.
  • Усредненный для лица 1 является 4.

таблица может выглядеть следующим образом: evaluator, evaluatee, rating, date.

С наилучшими пожеланиями

Майкл

+0

ли ваша таблица также суррогатный синтетический первичный ключ? Может быть, автоинкрементное целое число? Это сделает запрос более простым и более эффективным, если это так. –

+0

Какая версия SQL Server? – AakashM

ответ

0

Это можно сделать, но это может быть действительно гарри - SQL не был предназначен для сравнения строк, только столбцы. Я настоятельно рекомендую вам сохранить дополнительную таблицу, содержащую только самые последние данные, и сохранить остальные в таблице архива.

Если вы должны, сделайте это так, тогда мне понадобится полная структура таблицы, чтобы попытаться написать запрос для этого. В частности, мне нужно знать, какие уникальные индексы.

5

Это вполне возможно.

Давайте предположим, что ваша структура таблицы выглядит следующим образом:

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 

Или для всех Evaluatee годов, сгруппированные по Evaluatee:

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).

+0

+1 - Мне очень нравится этот подход и удалю мой ответ в знак уважения :) –

1
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