2016-09-20 2 views
1

У меня родитель posts стол и ребенок votes с posts.id10 и votes.post_id отношение. Я хочу посчитать средний рейтинг за каждый пост, но только за последние 50 голосов. Я знаю, как сделать это для всех голосов:Среднее значение для каждого родителя с условием

SELECT T1.`title`, (
    SELECT AVG(`vote`) 
    FROM `votes` 
    WHERE `votes`.`post_id` = T1.`id` 
) AS `average` 
FROM `posts` T1 
GROUP BY T1.`id` 

Я знаю, что это можно сделать с подзапроса:

SELECT T1.`title`, (
    SELECT AVG(`vote`) 
    FROM (
     SELECT `vote` FROM `votes` 
     WHERE `votes`.`post_id` = T1.`id` 
     ORDER BY `votes`.`id` DESC 
     LIMIT 10 
    ) AS T2 
) AS `average` 
FROM `posts` T1 
GROUP BY T1.`id` 

Но есть ошибка: Error in query (1054): Unknown column 'T1.id' in 'where clause'. T1 псевдоним недоступен в подзапросе. Есть идеи?

http://sqlfiddle.com/#!9/fb9341/2

ответ

1

Итак, вы хотите, чтобы выбрать те строки, которые находятся в самых последних 50 строк на пост. Существует множество ответов на переполнение стека для этого типа запросов, в основном под тегами или . Пример: How to SELECT the newest four items per category?

После того, как вы напишите этот запрос, вы можете поместить внутри подзапроса, как вы уже знаете, как писать, чтобы получить AVG(vote) за сообщение.


Re ваши комментарии:

Это то, что я имею в виду:

SELECT T1.title, AVG(V.vote) AS avg_vote 
FROM posts T1 
JOIN (
    SELECT v1.id, v1.post_id, v1.vote 
    FROM votes v1 
    LEFT OUTER JOIN votes v2 ON v1.post_id = v2.post_id and v1.id < v2.id 
    GROUP BY v1.id 
    HAVING COUNT(*) < 10 
) AS V ON T1.id = V.post_id 
GROUP BY T1.id; 

Выходные приведены данные в вашем SQLFiddle:

+---------+----------+ 
| title | avg_vote | 
+---------+----------+ 
| Title 1 | 5.4000 | 
| Title 2 | 4.2000 | 
+---------+----------+ 

Чтобы помочь РЕГИСТРИРУЙТЕСЬ в подзапроса , вы должны иметь индекс на votes по столбцам (post_id, id).


Вот еще одно решение, которое работает, не требуя уникальную колонку:

SELECT T1.title, AVG(V.vote) AS avg_vote 
FROM posts T1 
JOIN (
    SELECT * 
    FROM (
     SELECT v.*, @r := IF(@p = post_id, @r+1, 1) AS rownum, @p := post_id 
     FROM (SELECT @p:=null, @r:=0) AS _init 
     CROSS JOIN votes v 
     ORDER BY v.post_id, v.id DESC 
    ) AS t 
    WHERE t.rownum <= 10 
) AS V ON T1.id = V.post_id 
GROUP BY T1.id; 

Выхода же, как и предыдущий запрос.

+0

Да, но мне все равно нужно передать псевдоним в подзапрос, потому что 'AVG' создает дополнительный подзапрос. – hlcs

+0

Нет, нет. Выполняйте запрос top-50 на 'vote.post_id' без ссылки на внешний запрос. Сделайте это как подзапрос в предложении 'FROM'. Затем присоедините это к таблице 'posts'. –

+0

Я сделал это, и это очень медленно 4.5s. – hlcs

Смежные вопросы