web-dev-qa-db-ja.com

activerecordのサブクエリ

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を使用してこれを行うにはどうすればよいですか?

私は実際のサブクエリを必要とする/必要としない、Ruby回避策(いくつかのクエリを使用))。

78
gucki

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')

(「今」が参照しているバージョン番号はほとんどの場合3.2です)

118

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)" 

更新:コメントへの応答

さて、質問を誤解しました。 2つのクエリでデータベースにヒットしないように、選択する列名を明示的にサブクエリに一覧表示する必要があると考えています(これはActiveRecordが最も単純な場合に行うことです)。

副選択のprojectselectを使用できます。

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)

今回、あなたが望んでいたものを与えてくれたことを心から願っています!

40
Scott

私はこの質問に対する答えを自分で探していましたが、別のアプローチを思いつきました。私はそれを共有すると思った-それが誰かを助けることを願っています! :)

# 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で動作します

23
John

別の選択肢:

Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })
0
lobati

これは、Rails ActiveRecordを使用し、JOINを使用して、各クエリと結果に句を追加できるネストされたサブクエリの例です。

ネストされたinner_queryおよびouter_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("...")
   }
0
aabiro