activerecord中的子查询

guc*_*cki 78 subquery arel ruby-on-rails-3.1

使用SQL,我可以轻松地执行这样的子查询

User.where(:id => Account.where(..).select(:user_id))
Run Code Online (Sandbox Code Playgroud)

这会产生:

SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)
Run Code Online (Sandbox Code Playgroud)

我怎样才能使用rails'3 activerecord/arel/meta_where?

我确实需要/想要真正的子查询,没有ruby解决方法(使用多个查询).

Chr*_*lom 118

Rails现在默认执行此操作:)

Message.where(user_id: Profile.select("user_id").where(gender: 'm'))
Run Code Online (Sandbox Code Playgroud)

将生成以下SQL

SELECT "messages".* FROM "messages" WHERE "messages"."user_id" IN (SELECT user_id FROM "profiles" WHERE "profiles"."gender" = 'm')
Run Code Online (Sandbox Code Playgroud)

("现在"指的版本号最有可能是3.2)

  • @coorasse:如果你正在使用Rails 4,那么现在有一个[`not`条件](http://guides.rubyonrails.org/active_record_querying.html#not-conditions).通过调整[this post](http://pivotallabs.com/hacking-a-subselect-in-activerecord/)中的方法,我能够在Rails 3中完成它:`subquery = Profile.select("user_id") .where(性别:'m')).to_sql; Message.where('user_id NOT IN(#{subquery}))``基本上,`ActiveRecord`方法用于创建已完成的,正确引用的子查询,然后将其内联到外部查询中.主要的缺点是子查询参数不受约束. (12认同)
  • 如果条件不是,如何做同样的事情? (6认同)
  • 只是为了完成@12217关于Rails 4的观点,具体的语法不是`Message.where.not(user_id:Profile.select("user_id").其中(性别:'m'))` - 生成"NOT IN" "subselect.刚刚解决了我的问题.. (3认同)

Sco*_*ott 40

在ARel中,这些where()方法可以将数组作为参数来生成"WHERE id IN ..."查询.所以你所写的是沿着正确的方向.

例如,以下ARel代码:

User.where(:id => Order.where(:user_id => 5)).to_sql
Run Code Online (Sandbox Code Playgroud)

......相当于:

User.where(:id => [5, 1, 2, 3]).to_sql
Run Code Online (Sandbox Code Playgroud)

...将在PostgreSQL数据库上输出以下SQL:

SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)" 
Run Code Online (Sandbox Code Playgroud)

更新:回应评论

好的,所以我误解了这个问题.我相信您希望子查询显式列出要选择的列名,以便不用两个查询命中数据库(这是ActiveRecord在最简单的情况下所做的).

您可以使用projectselect您的子选择:

accounts = Account.arel_table
User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))
Run Code Online (Sandbox Code Playgroud)

...会产生以下SQL:

SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)
Run Code Online (Sandbox Code Playgroud)

我真诚地希望这次能给你你想要的东西!

  • 在误读问题后解决问题的+1 (9认同)

Joh*_*ohn 23

我自己正在寻找这个问题的答案,我想出了另一种方法.我以为我会分享它 - 希望它可以帮助别人!:)

# 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})")
Run Code Online (Sandbox Code Playgroud)

答对了!邦戈!

适用于Rails 3.1

  • 它只会在REPL中执行两次第一次查询,因为它在查询上调用to_s来显示它.它只会在您的应用程序中执行一次. (8认同)
  • 它执行第一次查询两次.最好做`subquery = Account.where(...).select(:id).to_sql``query = User.where("users.account_id IN(#{subquery})")` (4认同)