6

Nameさまざまな名前を含むActiveRecord という名前がありますLanguages

class Name < ActiveRecord::Base
  belongs_to :language

class Language < ActiveRecord::Base
  has_many :names

1 つの言語で名前を見つけるのは簡単です。

Language.find(1).names.find(whatever)

しかし、言語 1 と言語 2 の両方が同じ名前を持つ一致するペアを見つける必要があります。SQL では、これは単純な自己結合を呼び出します。

SELECT n1.id,n2.id FROM names AS n1, names AS n2
  WHERE n1.language_id=1 AND n2.language_id=2
    AND n1.normalized=n2.normalized AND n1.id != n2.id;

ActiveRecord でこのようなクエリを実行するにはどうすればよいですか? たまたま何かと一致する言語 1 の名前のリストだけでなく、名前のペア (= 一致の両側) を見つける必要があることに注意してください。

ボーナス ポイントについては、フィールドに SQL ワイルドカードが含まれている可能性があるためn1.normalized=n2.normalized、に置き換えます。n1.normalized LIKE n2.normalized

また、データを別の方法でモデル化するというアイデアにもオープンですが、できれば言語ごとに別々のテーブルを用意することは避けたいと思っています。

4

2 に答える 2

8

これを試して:

ids = [1,2]
Name.all(:select    => "names.id, n2.id AS id2",
         :joins     => "JOIN names AS n2 
                              ON n2.normalized = names.normalized AND 
                                 n2.language_id != names.language_id AND
                                 n2.language_id IN (%s)" % ids.join(','),
         :conditions => ["names.language_id IN (?)", ids]
).each do |name|
  p "id1 : #{name.id}"
  p "id2 : #{name.id2}"
end

PS: 結合条件に渡されたパラメーターを必ずサニタイズしてください。

于 2010-09-05T04:02:55.003 に答える
1

has_many/belongs_to の代わりに、Language と Name の間に多対多の関係を使用したいと思われるかもしれません。

>> Language.create(:name => 'English')
 => #<Language id: 3, name: "English", created_at: "2010-09-04 19:15:11", updated_at: "2010-09-04 19:15:11"> 
>> Language.create(:name => 'French')
 => #<Language id: 4, name: "French", created_at: "2010-09-04 19:15:13", updated_at: "2010-09-04 19:15:13"> 
>> Language.first.names << Name.find_or_create_by_name('Dave')
 => [#<Name id: 3, name: "Dave", language_id: 3, created_at: "2010-09-04 19:16:50", updated_at: "2010-09-04 19:16:50">] 
>> Language.last.names << Name.find_or_create_by_name('Dave')
 => [#<Name id: 3, name: "Dave", language_id: 4, created_at: "2010-09-04 19:16:50", updated_at: "2010-09-04 19:16:50">]
>> Language.first.names.first.languages.map(&:name)
 => ["English", "French"] 

この余分なレベルの正規化により、実行しようとしていることがより簡単になります。

于 2010-09-04T19:26:27.573 に答える