使用SQL,我可以轻松地执行这样的子查询
User.where(:id => Account.where(..).select(:user_id))
这将产生:
SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)
如何使用Rails的3 activerecord / arel / meta_where做到这一点?
我确实需要/想要真正的子查询,没有红宝石的解决方法(使用几个查询)。
Answers:
Rails现在默认执行此操作:)
Message.where(user_id: Profile.select("user_id").where(gender: 'm'))
将产生以下SQL
SELECT "messages".* FROM "messages" WHERE "messages"."user_id" IN (SELECT user_id FROM "profiles" WHERE "profiles"."gender" = 'm')
(“ now”所指的版本号很可能是3.2)
Message.where.not(user_id: Profile.select("user_id").where(gender: 'm'))
-生成“ NOT IN”子选择。刚解决了我的问题
在ARel中,这些where()
方法可以将数组作为参数,这将生成“ WHERE id IN ...”查询。因此,您所写的内容正确无误。
例如,以下ARel代码:
User.where(:id => Order.where(:user_id => 5)).to_sql
...相当于:
User.where(:id => [5, 1, 2, 3]).to_sql
...将在PostgreSQL数据库上输出以下SQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)"
更新:回应评论
好吧,所以我误解了这个问题。我相信您希望子查询显式列出要选择的列名,以便不通过两个查询命中数据库(这是ActiveRecord在最简单的情况下所做的事情)。
您可以使用project
为select
您的子选择:
accounts = Account.arel_table
User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))
...这将产生以下SQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)
衷心希望我这次能给您您想要的东西!
我自己一直在寻找这个问题的答案,所以我想出了另一种方法。我只是想分享一下-希望它能对某人有所帮助!:)
# 1. Build you subquery with AREL.
subquery = Account.where(...).select(:id)
# 2. Use the AREL object in your query by converting it into a SQL string
query = User.where("users.account_id IN (#{subquery.to_sql})")
答对了!班戈!
适用于Rails 3.1
subquery = Account.where(...).select(:id).to_sql
query = User.where("users.account_id IN (#{subquery})")
这是一个使用rails ActiveRecord和JOIN的嵌套子查询的示例,您可以在其中添加每个子句以及结果的子句:
您可以在模型文件中添加嵌套的inner_query范围和external_query范围,并使用...
inner_query = Account.inner_query(params)
result = User.outer_query(params).joins("(#{inner_query.to_sql}) alias ON users.id=accounts.id")
.group("alias.grouping_var, alias.grouping_var2 ...")
.order("...")
范围的示例:
scope :inner_query , -> (ids) {
select("...")
.joins("left join users on users.id = accounts.id")
.where("users.account_id IN (?)", ids)
.group("...")
}