Допустим, вы запускаете его один раз в первый день месяца и кэшируете результаты, поскольку подсчет голосов на каждой странице бесполезен.
Первая арифметика с некоторыми датами:
SELECT now(),
date_trunc( 'month', now() ) - '1 MONTH'::INTERVAL,
date_trunc( 'month', now() );
now | ?column? | date_trunc
-------------------------------+------------------------+------------------------
2011-07-07 16:24:38.765559+02 | 2011-06-01 00:00:00+02 | 2011-07-01 00:00:00+02
ОК, мы получили границы для диапазона даты и времени «последний месяц».
Теперь нам нужна некоторая оконная функция для получения первых строк по полу:
SELECT * FROM (
SELECT *, rank( ) over (partition by gender order by score desc )
FROM (
SELECT user_id, count(*) AS score FROM pref_rep
WHERE nice=true
AND last_rated >= date_trunc( 'month', now() ) - '1 MONTH'::INTERVAL
AND last_rated < date_trunc( 'month', now() )
GROUP BY user_id) s1
JOIN users USING (user_id)) s2
WHERE rank=1;
Обратите внимание, это может дать вам несколько строк в случае экс-aequo.
РЕДАКТИРОВАТЬ:
У меня есть хорошее предложение наложить метки времени на строки, чтобы
найти записи за последний месяц (не за последние 30 дней)
date_trunc () работает намного лучше.
Если вы сделаете 2 запроса, вам придется выполнить count () дважды. Поскольку пользователи потенциально могут многократно голосовать за других пользователей, эта таблица, вероятно, будет более крупной, поэтому сканирование ее один раз - хорошая вещь.
Вы не можете "оставить присоединение к таблице пользователей и к внешней части запроса", потому что вам нужны гендеры ...
Запрос выше занимает около 30 мс с 1k пользователей и 100k голосов, так что вы определенно захотите его кешировать.