2016-10-19 2 views
2

В моем приложении Rails я разрешаю пользователям «голосовать» за события. Некоторые события - это только одна конкретная дата, и некоторые события продолжаются.Rails & ActiveRecord: сложный SQL-запрос

Я пытаюсь построить запрос, который отображает все события, которые либо A) пользователь имеет не на голосование ИЛИ В) текущих событий пользователь проголосовал на над 1 месяц назад.

Событие имеет «текущую» булевскую колонку. Событие has_many голосов. И пользователь has_many голосов.

Это мой запрос, чтобы получить все события у пользователя не проголосовали. Оно работает.

scope :unvoted, ->(user_id) { 
    joins(
     "LEFT OUTER JOIN votes ON votes.event_id = events.id AND " + 
     "votes.user_id = #{user_id}" 
     ).where("votes.id IS NULL") 
    } 

Я пытаюсь добавить или часть запроса. Мне нужно включить «текущие» события, которые пользователь проголосовал более месяца назад. Вот что я пробовал:

scope :unvoted, ->(user_id) { 
    joins(
      "LEFT OUTER JOIN votes ON votes.event_id = events.id AND " + 
      "votes.user_id = #{user_id}" 
     ) 
     .where(
       "votes.id IS NULL OR events.ongoing = ? AND MAX(votes.created_at) < ?", true, 1.month.ago 
     ) 
    } 

Но это дает мне ошибку:

PG::GroupingError: ERROR: aggregate functions are not allowed in WHERE

Так что я попытался это:

scope :unvoted, ->(user_id) { 
    joins(
      "LEFT OUTER JOIN votes ON votes.event_id = events.id AND " + 
      "votes.user_id = #{user_id}" 
          ). 
    having('votes.id IS NULL OR events.ongoing = ? AND MAX("votes"."created_at") < ?', true, 1.month.ago). 
    group("events.id") 
    } 

Но это дает мне ошибку:

ERROR: column must appear in the GROUP BY clause or be used in an aggregate function

Каков правильный запрос, который я ищу для?

+0

Для сложных вопросов SQL убедитесь, маркировать их с СУРБД и создать хороший пример таблиц и ожидаемого результата. Есть гораздо больше последователей для тегов postgres и mysql, чем, например, ruby-on-rails, и вы, как правило, получаете более качественные ответы. – max

+0

Какова ваша цель использования MAX? Просто получить последнюю версию из сопоставленных записей или заказать записи от нового до старого? – YTorii

+0

@YTorii Мне нужно получить последнюю из сопоставленных записей, чтобы узнать, было ли это создано за месяц назад –

ответ

0

EDIT: Похоже, что нет способа писать ИЛИ с ИМЕЙСОМ. Вы можете использовать подзапрос (Using 'OR' between HAVING and WHERE clause in MySQL?) или UNION, чтобы решить эту проблему.

Или чтобы все было просто, вы можете разбить это на два вопроса.

только ActiveRecord решение: https://stackoverflow.com/a/15413611/5213979

+0

Хммм .. Я не уверен, что это сработает, потому что оно вернет все события с голосованием старше 1 месяца назад. Но я хочу только вернуть события, когда последнее голосование было более 1 месяца назад –

+0

@JacksonCunningham Хорошо, понял, что. –

+0

Можете ли вы привести пример этого с двумя запросами? Мне нравится этот подход, но не уверен, как это сделать с помощью «ИЛИ», –

0

Вы можете попробовать

scope :unvoted, ->(user_id) { 
    joins(
     "LEFT OUTER JOIN votes ON votes.event_id = events.id AND " + 
     "votes.user_id = #{user_id}" 
    ).where(
     "votes.id IS NULL OR events.ongoing = ?", true      
    ).select('events.*, MAX(votes.created_at) as created_at') 
    ).group('events.id') 
} 

, а затем в вашем методе, вы можете сделать

Event.unvoted.select{|e| e.created_at < 1.month.ago}

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