PostgreSQL: пытается найти мисс и мистер прошлого месяца с самым высоким рейтингом - PullRequest
2 голосов
/ 07 июля 2011

На моем веб-сайте Drupal пользователи могут оценивать друг друга, и эти отметки времени хранятся в таблице pref_rep :

# select id, nice, last_rated from pref_rep where nice=true
  order by last_rated desc limit 7;
           id           | nice |         last_rated
------------------------+------+----------------------------
 OK152565298368         | t    | 2011-07-07 14:26:38.325716
 OK452217781481         | t    | 2011-07-07 14:26:10.831353
 OK524802920494         | t    | 2011-07-07 14:25:28.961652
 OK348972427664         | t    | 2011-07-07 14:25:17.214928
 DE11873                | t    | 2011-07-07 14:25:05.303104
 OK335285460379         | t    | 2011-07-07 14:24:39.062652
 OK353639875983         | t    | 2011-07-07 14:23:33.811986

Также я сохраняю пол каждого пользователя в таблице pref_users :

# select id, female from pref_users limit 7;
       id       | female
----------------+--------
 OK351636836012 | f
 OK366097485338 | f
 OK251293359874 | t
 OK7848446207   | f
 OK335478250992 | t
 OK355400714550 | f
 OK146955222542 | t

Я пытаюсь создать 2 блока Drupal с надписью "Мисс в прошлом месяце" и "Мистер в прошлом месяце", но мой вопрос не о Drupal, поэтому, пожалуйста, не перемещайте его на drupal.stackexchange.com; -)

Мой вопрос касается SQL: как мне найти пользователя с наибольшим количеством хороших - и это за последний месяц? У меня было бы 2 запроса - один для женщин и один для не женщин.

Использование PostgreSQL 8.4.8 / CentOS 5.6 и SQL иногда так сложно: -)

Спасибо! Alex

UPDATE:

У меня есть хорошее предложение привести метки времени к строкам, чтобы найти записи за последний месяц (не за последние 30 дней)

UPDATE2:

Я закончил сравнение строк:

select r.id,
        count(r.id),
        u.first_name,
        u.avatar,
        u.city
from pref_rep r, pref_users u where
        r.nice=true and
        to_char(current_timestamp - interval '1 month', 'IYYY-MM') =
        to_char(r.last_rated, 'IYYY-MM') and
        u.female=true and
        r.id=u.id
group by r.id , u.first_name, u.avatar, u.city
order by count(r.id) desc
limit 1

1 Ответ

0 голосов
/ 07 июля 2011

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

Первая арифметика с некоторыми датами:

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 голосов, так что вы определенно захотите его кешировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...