【问题标题】:Where condition when joining to a table twice两次加入表时的条件
【发布时间】:2019-08-27 18:22:46
【问题描述】:

我有一个 Bug 课程,里面有用户表中的分析师和提交者。

class Bug < ApplicationRecord
  belongs_to :analyst, class_name: 'User'
  belongs_to :committer, class_name: 'User'
  ...
end

我有一个搜索,其中可能包括搜索分析师、提交者或两者兼而有之。

我如何知道我的表将使用哪个表别名活动记录?


如果我像这样搜索分析师,我得到的 SQL,

analyst = 'abenge'
Bug.joins(:analyst).where(users: {username: analyst}).to_sql
=> "SELECT `bugs`.* FROM `bugs`
    INNER JOIN `users` ON `users`.`id` = `bugs`.`analyst_id`
    WHERE `users`.`username` = 'abenge'"

同样,如果我搜索提交者,我得到的 SQL,

analyst = 'amcdonnell'
Bug.joins(:committer).where(users: {username: committer}).to_sql
=> "SELECT `bugs`.* FROM `bugs`
    INNER JOIN `users` ON `users`.`id` = `bugs`.`committer_id`
    WHERE `users`.`username` = 'amcdonnell'"

但如果我同时搜索两者,则 where 调用不能同时使用 users。表有别名,我需要使用别名。

Bug.joins(:analyst, :committer)
   .where(users: {username: committer})
   .where(committers_bugs: {username: committer}).to_sql
=> "SELECT `bugs`.* FROM `bugs`
    INNER JOIN `users` ON `users`.`id` = `bugs`.`analyst_id`
    INNER JOIN `users` `committers_bugs` ON `committers_bugs`.`id` = `bugs`.`committer_id`
    WHERE `users`.`username` = 'abenge'
      AND `committers_bugs`.`cvs_username` = 'amcdonnell'"

所以我的问题是,“我如何知道在 where 子句中为提交者的用户名使用哪个键,在一种情况下是 users 而在另一种情况下是 committers_bugs


我希望代码是这样的:

relation = Bug

analyst = params['analyst']
if analyst.present?
  relation = relation.joins(:analyst).where(user: {username: analyst})
end

committer = params['committer']
if committer.present?
  relation = relation.joins(:committer).where(committers_bugs: {username: committer})
end

问题是,如果他们同时搜索以上内容,则上述方法有效。但是,如果他们搜索提交者而不搜索分析师,则活动记录使用users 作为表名来连接committer

【问题讨论】:

  • 我从建议的答案中获得了一些见解,这让我意识到添加 SQL 会使我的问题更清楚,所以我编辑了我的问题。

标签: ruby-on-rails join activerecord


【解决方案1】:

如果您的每个“Bugs”都与两个用户相关,则您的 Bug 表需要通过两个不同的外键关联:

迁移

class CreateBug < ActiveRecord::Migration
  def change
    create_table :bugs do |t|
      # ...
      t.references :analyst_id, index: true, foreign_key: {to_table: :users}
      t.references :committer_id, index: true, foreign_key: {to_table: :users}
    end
  end
end

型号

class Bug < ActiveRecord
  belongs_to :analyst, class_name: 'User', foreign_key: 'analyst_id'
  belongs_to :committer, class_name: 'User', foreign_key: 'committer_id'
end

【讨论】:

  • 这已经对我有用了。我在表中有两个外键,两个belongs_to 语句默认为正确的外键。问题是如何指定 where 子句。
  • 我不确定我是否理解,您可能想要在 SQL 级别上获取 JOIN 语句而不是 WHERE 语句中的条件,以便获得正确的过滤记录?
  • 我是说当我加入两个关联时,它使用committers_bugs 作为第二个加入的表别名,但是当我加入@987654325 @association,我需要在where中使用的表是users。那么我怎么知道是哪个
【解决方案2】:

提交者和分析师都只是同一张表的 SQL 别名。如果您在 WHERE 语句中使用它们中的任何一个,结果将始终相同。如果要将条件限制为特定的 JOIN,则必须将该条件添加到 ON 语句中。

我喜欢使用的一个选项是在关系中添加一个 lambda:

class Bug < ActiveRecord
  belongs_to :analyst, -> { where(username: 'Haruki Murakami') }, class_name: 'User'
end

返回

SELECT  "bugs".* 
FROM "bugs" 
  INNER JOIN "users" 
    ON "bugs"."user_id" = "users"."id" AND "users"."username" = $1 
LIMIT $2

这个问题是,只要我有搜索,你就不能将参数传递给这个 lambda,但你仍然可以做一些不太漂亮但功能齐全的事情,比如:

Bug.joins("LEFT JOIN users ON bugs.user_id = user.id AND users.username = ?", params['committer'])

...在您的示例中必须实现为:

relation = Bug

analyst = params['analyst']
if analyst.present?
  relation = relation.joins("LEFT JOIN users A ON bugs.analyst_id = A.id AND A.username = ?", analyst)
end

committer = params['committer']
if committer.present?
  relation = relation.joins("LEFT JOIN users C ON bugs.committer_id = C.id AND C.username= ?", committer)
end

也许您想将此代码移动到您的模型中,例如:

class Bug < ActiveRecord

  def self.who_analyses(name)
    Bug.joins("LEFT JOIN users A ON bugs.analyst_id = A.id AND A.username = ?", name)
  end

  def self.who_have_commited(name)
    Bug.joins("LEFT JOIN users C ON bugs.committer_id = C.id AND C.username= ?", name)
  end

end

analyst   = params['analyst']
committer = params['committer']

if analyst.present?
  relation = relation.who_analyses(analyst)
end

if committer.present?
  relation = relation.who_have_commited(committer)
end

【讨论】:

  • 这个答案的问题是你使用了bugs.user_id,但是bugs表没有user_id外键,它有analyst_idcommitter_id。我想在搜索分析师时加入analyst_id,在搜索提交者时加入committer_id,并通过两次加入同一个表来搜索两者来加入两者。当两者都存在时,我需要给表名起别名以区分条件与哪个联接有关,
  • 我编辑了我的问题,添加了我的问题所涉及的 SQL,使问题更加清晰。
猜你喜欢
  • 2014-09-15
  • 1970-01-01
  • 2011-12-01
  • 2012-04-21
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多