【问题标题】:Rails User.joins.not(...) in Active Record?活动记录中的 Rails User.joins.not(...)?
【发布时间】:2014-05-30 15:22:41
【问题描述】:

我希望在单个 sql 查询中查询所有没有评论的用户?

型号:

class User < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
end

所以我想要相反的:

User.joins.(:comments).group('users.id')

但不是这样:(因为它会生成两个查询)

User.where.not(id: Comment.pluck(:user_id))

也许是这样的?

User.joins.not.(:comments).group('users.id')

感谢您的任何意见!

【问题讨论】:

  • 试试User.includes(:comments).where(comments: { id: nil })
  • @MrYoshiji 这太棒了,比公认的答案更正确。谢谢。
  • 注意:在 Rails 6.1 中,您可以只使用 User.where.missing( :comments )。请参阅edgeapi.rubyonrails.org/classes/ActiveRecord/QueryMethods/… 和这个 SO 答案stackoverflow.com/a/5570221/293280
  • 虽然@MrYoshiji 提出的解决方案可能有效(如果包含在内部使用 eager_load 处理),但不能保证有效。如果 AR 执行预加载,则不会发生连接,查询将失败。如果您需要进行连接,请使用连接(或在现代 rails left_outer_joins 上)。

标签: sql ruby-on-rails activerecord ruby-on-rails-4


【解决方案1】:

您可以通过以下方式完成此操作:

User.includes(:comments).where.not(comments: { id: nil })

这将产生类似于以下内容的原始 SQL:

SELECT DISTINCT `users`.`*` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`id` IS NULL

要通过子查询完成此操作,请参阅以下答案。

旧答案

你可以这样做

User.where.not(id: Comment.select(:user_id))

如果您想要一个(尽管是嵌套的)查询。

否则,请查看 http://guides.rubyonrails.org/active_record_querying.html#joining-tables 以了解如何使用外连接。

【讨论】:

  • 它会使用子查询还是 2 个不同的查询?
  • @thethanghn 在这种情况下,它将导致子查询。每当您将ActiveRecord::Relation(这是selectwhere 等活动记录查询方法返回的内容)传递给查询方法时,都会产生子查询。
  • 为什么在User.includes(:comments).where.not(comments: { id: nil }) 中使用not?这对我来说似乎是错误的。你希望条件是id IS NULL,而不是它的倒数。
【解决方案2】:

如果你使用postgresql,你可以这样做

User.joins("LEFT join comments c on users.id = c.comment_id").
select("users.id").
group("users.id").
having("count(users.id) = 1")

这将生成这个查询

select u.id from users u
LEFT join comments c 
on c.comment_id = u.id
group by u.id
having count(u.id) = 1

这个查询没有生成两个 SQL(也没有嵌套的 sql),上面的答案确实如此。

【讨论】:

  • 该查询中的cu 代表什么?
  • @thiebo "c" 是 cmets 表的别名,而 "u" 是 users 表的别名。您可以找到有关 SQL 别名的更多信息here
  • 我成功地使用了这个,除了我改变了有子句来测试是否没有子记录。类似.having("count(comment.id) = 0")
【解决方案3】:

另一个简单的解决方案 User.where("id NOT IN (?)", Comment.pluck(:user_id))

【讨论】:

  • 适用于 Rails 控制台中的快速即席查询,尤其是在表之间没有模型关系的情况下。但是...这不是生产代码的好方法。
  • 只是添加到 David Hempy,它不适合生产代码的原因是因为它需要运行 两个 查询:一个获取 user_ids 的所有Comment 记录,然后是两个以获取不在那些user_ids 中的所有User 记录。在单个查询中执行此操作是可能且更好的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多