как изменить сложный запрос find_by_sql с объединением в рельсы 3 - PullRequest
0 голосов
/ 27 сентября 2010

вот текущий запрос:

@feed = RatedActivity.find_by_sql(["(select *, null as queue_id, 3 as model_table_type from rated_activities where user_id in (?)) " +  
"UNION (select *, null as queue_id, null as rating, 2 as model_table_type from watched_activities where user_id in (?)) " +  
"UNION (select *, null as rating, 1 as model_table_type from queued_activities where user_id in (?)) " +"ORDER BY activity_datetime DESC limit 100", friend_ids, friend_ids, friend_ids])

Теперь, это немного клудж, так как на самом деле есть модели, настроенные для:

class RatedActivity < ActiveRecord::Base
  belongs_to :user
  belongs_to :media
end

class QueuedActivity < ActiveRecord::Base
  belongs_to :user
  belongs_to :media
end

class WatchedActivity < ActiveRecord::Base
  belongs_to :user
  belongs_to :media
end

хотел бы знать, как использовать activerecord в rails 3.0 для достижения того же самого, что и с сумасшедшим союзом, который у меня есть.

Ответы [ 2 ]

2 голосов
/ 27 сентября 2010

Похоже, вам следует объединить эти три отдельные модели в одну модель. Статусы, такие как «наблюдаемый», «поставленный в очередь» или «оцененный», тогда все неявно основаны на атрибутах этой модели.

class Activity < ActiveRecord::Base
  belongs_to :user
  belongs_to :media

  scope :for_users, lambda { |u|
    where("user_id IN (?)", u)
  }
  scope :rated, where("rating IS NOT NULL")
  scope :queued, where("queue_id IS NOT NULL")
  scope :watched, where("watched IS NOT NULL")
end

Затем вы можете позвонить Activity.for_users(friend_ids), чтобы получить все три группы, как вы пытаетесь выполнить выше ... или вы можете позвонить Activity.for_users(friend_ids).rated (или поставить в очередь или наблюдать), чтобы получить только одну группу. Таким образом, вся ваша логика активности консолидируется в одном месте. Ваши запросы становятся проще (и эффективнее), и вам не нужно поддерживать три разные модели.

1 голос
/ 27 сентября 2010

Я думаю, что ваше текущее решение в порядке в случае устаревшей БД. Как собственный запрос, он также наиболее эффективен, поскольку ваша СУБД выполняет всю тяжелую работу (объединение, сортировка, ограничение).

Если вы действительно хотите избавиться от SQL UNION без изменения схемы, вы можете переместить объединение в сумму массива Ruby - но это может быть медленнее.

result = RatedActivity.
             select("*, null as queue_id, 3 as model_table_type").
             where(:user_id=>friend_ids).
             limit(100).all +
         QueuedActivity...

Наконец, вам нужно отсортировать и ограничить этот продукт с помощью

result.sort(&:activity_datetime)[0..99]

Это просто подтверждение концепции, поскольку вы видите, что некоторые пункты неэффективны (3 запроса, сортировка в Ruby, ограничение). Я бы остался с find_by_sql.

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