Ruby on rails 具有ActiveRecord的表上的自联接

Ruby on rails 具有ActiveRecord的表上的自联接,ruby-on-rails,activerecord,mysql,self-join,Ruby On Rails,Activerecord,Mysql,Self Join,我有一个名为Name的ActiveRecord,其中包含各种语言的名称 但是我需要找到匹配对,其中语言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; 如何使用ActiveRecor

我有一个名为Name的ActiveRecord,其中包含各种语言的名称

但是我需要找到匹配对,其中语言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中碰巧与某个内容匹配的名称列表

对于奖励积分,将n1.normalized=n2.normalized替换为n1.normalized,就像n2.normalized一样,因为该字段可能包含SQL通配符


我也愿意以不同的方式对数据进行建模,但如果可能的话,我更愿意避免为每种语言使用单独的表。

听起来您可能希望在语言和名称之间使用多对多关系,而不是has_many/behing_

>> 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"] 
这一额外的规范化级别应该会使您尝试做的事情更容易。

尝试以下方法:

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:确保清理传递给联接条件的参数。

Ooh,很有趣。问题是,例如,芬兰语的“Joni”和希伯来语的“Yoni”实际上是不同的名称,在原始脚本中有不同的属性拼写,等等,只是碰巧有相同的规范化名称字段,而不仅仅是一个名称。好吧,在修复了一个小的拼写错误后,这当然有效,应该是:joins=>JOIN names as。。。,但它只返回带有id2的语言1中的Name对象。在语言2中为名称获取对象需要为每个匹配调用Name.findname.id2,这会导致相当大的性能损失。有没有办法解决这个问题?好的,在添加和名称后,将返回两种语言中所有匹配项的列表。language_id!=n2.language_id筛选出自匹配项,但这是一个慢得多的查询,它返回一个巨大的列表,而不是对列表-我仍然需要使用Name.findname.id2来计算名称的匹配对。列表中返回了多少行?理想情况下,如果同一个键没有重复的条目,则应该返回一行。在你的第二次发现中,你想得到什么数据?您可以更新选择列表,从名称表中添加所需的字段。我已经更新了答案。也许这一次它会起作用。
>> 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"] 
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