2009-04-25 3 views
0

У меня есть отношения таблица:Невозможно определить: объединяет условия в has_many отношения?

create_table "animal_friends", :force => true do |t| 
    t.integer "animal_id" 
    t.integer "animal_friend_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    t.integer "status_id",  :default => 1 
    end 

связывающие животное к другим. Лучший способ извлечь объединения в SQL является:

SELECT animals.* 
from animals join animal_friends as af 
    on animals.id = 
    case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end 
WHERE #{id} in (af.animal_id, af.animal_friend_id) 

И я не могу найти способ, чтобы создать правильную has_many отношения в рельсах с этим. По-видимому, нет способа обеспечить условия соединения для has_many.

Я в настоящее время использую finder_sql:

has_many :friends, :class_name => "Animal", :finder_sql => 'SELECT animals.* from animals join animal_friends as af on animals.id = case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end ' + 
'WHERE #{id} in (af.animal_id, af.animal_friend_id) and status_id = #{Status::CONFIRMED.id}' 

, но этот метод имеет большой недостаток ломки ActiveRecord магии. Например:

@animal.friends.first 

выполнит finder_sql без ограничений, выборка тысячи строк, а затем взяв первый из массива (и потери нескольких секунд драгоценных /REQ).

Я предполагаю, что это недостающая функция из AR, но я хотел бы, чтобы быть уверенными первыми :) Спасибо

ответ

2

Вы можете решить это на уровне базы данных с представлением, которое в любом случае было бы правильным методом.

CREATE VIEW with_inverse_animal_friends (
    SELECT id, 
     animal_id, 
     animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
    UNION 
    SELECT id, 
     animal_friend_id AS animal_id, 
     animal_id AS animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
) 

Если вы не хотите иметь двойные записи для друзей с отношениями оба пути вы могли бы сделать это:

CREATE VIEW unique_animal_friends (
    SELECT MIN(id), animal_id, animal_friend_id, MIN(created_at), MAX(updated_at), MIN(status_id) 
    FROM 
    (SELECT id, 
      animal_id, 
      animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends 
    UNION 
    SELECT id, 
      animal_friend_id AS animal_id, 
      animal_id AS animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends) AS all_animal_friends 
    GROUP BY animal_id, animal_friend_id 
) 

Вы должны были бы способ решить, какой status_id использовать в случае, если есть две противоречащие друг другу из них. Я выбрал MIN (status_id), но это, вероятно, не то, что вы хотите.

В Rails вы можете сделать это прямо сейчас:

class Animal < ActiveRecord::Base 
    has_many :unique_animal_friends 
    has_many :friends, :through => :unique_animal_friends 
end 

class UniqueAnimalFriend < ActiveRecord::Base 
    belongs_to :animal 
    belongs_to :friend, :class_name => "Animal" 
end 

Это из моей головы и не тестируются. Кроме того, вам может понадобиться некоторый плагин для обработки изображений в рельсах (например, «redhillonrails-core»).

+0

Спасибо, мне нравится идея! Я проверю, как только смогу, и дам вам знать. – Gravis

+0

Извините за задержку loooooooong, прежде чем ответить на это, я, наконец, успел проверить ваше решение, и он работает как шарм, из коробки. – Gravis

0

два пути, чтобы создать многие ко многим отношений в активной записи являются has_and_belongs_to_many и has_many: через. This site критикует различия между ними. Эти методы не нужно писать SQL.

+0

привет, спасибо за ссылку! В любом случае, habtm не допускает каких-либо условий соединения :( Поэтому мне нужно будет выполнить соединение, используя: условия, и поскольку у меня есть более 100 000 строк (~ 400 000), я хочу иметь действительно оптимизированный SQL запрос. Используя habtm, я получаю только половину результатов, так как я ищу отношения в foreign_key AND association_foreign_key. спасибо – Gravis

1

Существует плагин, который делает то, что вы хотите.

Об авторе here.

Существует альтернатива here.

Оба позволяют выполнить условия соединения и просто используют ленивую инициализацию. Таким образом, вы можете использовать динамические условия. Я нахожу прежнего красивее, но вы можете использовать последнее, если вы не хотите устанавливать плагины.

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