活动记录查询,将通过has_many关系检查特定字段的存在?

时间:2015-09-29 19:33:05

标签: ruby-on-rails activerecord

class GameSystem < ActiveRecord::Base
  has_many :cartridges
end

class Cartridge < ActiveRecord::Base
  belongs_to :game_system
end

我希望能够做到:

 GameSystem.joins(:cartridges)
   .where({:cartridge => { :name => 'Dragons Lair', :publisher => 'Cinematronics' })
   .where({:cartridge => { :name => 'Zaxxon', :publisher => 'Sega' })
   .limit(1)

换句话说,告诉我是否存在一个游戏系统,其中有一个由Cinematronics提供的Dragon's Lair硒鼓,以及一个名为Zaxxon by Sega的硒鼓......

是否有一种积极的记录友好的方式来做到这一点?

4 个答案:

答案 0 :(得分:1)

您需要执行两个单独的JOINS或子查询。 如果你想要更多的Activerecord友好,你可以使用自动JOIN和自己编写的一个,如下所示:

GameSystem.joins(:cartridges)
      .joins('JOIN cartridges AS c2 ON c2.game_system_id = game_systems.id')
      .where('cartridges.name = ? AND cartridges.publisher = ?', 'Dragons Lair', 'Cinematronics')
      .where('c2.name = ? AND c2.publisher = ?', 'Zaxxon', 'Sega')

或者使用子查询,它看起来像这样:

GameSystem.joins(:cartridges)
.where('cartridges.name = ? AND cartridges.publisher = ?', 'Dragons Lair', 'Cinematronics')
.where(id: GameSystem.joins(:cartridges)
  .where('cartridges.name = ? AND cartridges.publisher = ?', 'Zaxxon', 'Sega'))

子查询更具可读性和清晰度,但有时根据您的数据库引擎可能会降低性能。你可以在这个非常完整的主题上阅读它,并决定你喜欢哪一个:Join vs. sub-query

答案 1 :(得分:0)

稍微触摸SQL,您可以试试这个:

GameSystem.joins('JOIN cartridges AS cartridges1 ON cartridges1.game_system_id = game_systems.id')
    .joins('JOIN cartridges AS cartridges2 ON cartridges2.game_system_id = game_systems.id')
    .where('cartridges1.name = ? AND cartridges1.publisher = ?', 'Dragons Lair', 'Cinematronics')
    .where('cartridges2.name = ? AND cartridges2.publisher = ?', 'Zaxxon', 'Sega').limit(1)

答案 2 :(得分:0)

由于您需要2种不同类型的墨盒名称,因此需要OR

GameSystem.joins(:cartridges)
    .where("(cartridges.name = ? AND cartridges.publisher = ?) OR (cartridges.name = ? AND cartridges.publisher = ?)", 'Dragons Lair', 'Cinematronics', 'Zaxxon', 'Sega')

答案 3 :(得分:0)

要在子记录上过滤两个条件,您应该使用两个连接。 您无法通过单一连接实现此目的(即使使用纯SQL)。 ActiveRecord只是标准SQL的包装。

所以看起来应该是这样的:

GameSystem.joins('JOIN cartridges c1 ON c1.game_system_id = game_systems.id')
          .joins('JOIN cartridges c2 ON c2.game_system_id = game_systems.id')
          .where('c1.name = ? AND c1.publisher = ?', 'Dragons Lair', 'Cinematronics')
          .where('c2.name = ? AND c2.publisher = ?', 'Zaxxon', 'Sega')
          .any?
          # => true/false output
          # you can also use .to_a
          # or continue filtering as well