使用 ActiveRecord 在表上自连接

发布于 2024-09-17 20:42:48 字数 816 浏览 10 评论 0原文

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

class Name < ActiveRecord::Base
  belongs_to :language

class Language < ActiveRecord::Base
  has_many :names

查找一种语言的名称非常容易:

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 中的名称列表。

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

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

I have an ActiveRecord called Name which contains names in various Languages.

class Name < ActiveRecord::Base
  belongs_to :language

class Language < ActiveRecord::Base
  has_many :names

Finding names in one language is easy enough:

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

But I need to find matching pairs where both language 1 and language 2 have the same name. In SQL, this calls for a simple self-join:

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;

How can I do a query like this with ActiveRecord? Note that I need to find pairs of names (= both sides of the match), not just a list of names in language 1 that happens to match with something.

For bonus points, replace n1.normalized=n2.normalized with n1.normalized LIKE n2.normalized, since the field may contain SQL wildcards.

I'm also open to ideas about modeling the data differently, but I'd prefer to avoid having separate tables for each language if I can.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦罢 2024-09-24 20:42:48

试试这个:

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

Try this:

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: Make sure you sanitize the parameters passed to the join condition.

晨敛清荷 2024-09-24 20:42:48

听起来您可能想在 Language 和 Name 之间使用多对多关系,而不是 has_many/belongs_to。

>> 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"] 

这种额外的标准化水平应该会让您尝试做的事情变得更容易。

It sounds like you might want to use a many-to-many relationship between Language and Name instead of has_many/belongs_to.

>> 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"] 

This extra level of normalization should make what you are trying to do easier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文