Подзапросы в 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?

Мне нужны/нужны реальные подзапросы, нет рубиновых обходных решений (с использованием нескольких запросов).

Ответ 1

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

Ответ 2

В 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

... выводит следующий SQL-запрос в базе данных PostgreSQL:

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)

Я искренне надеюсь, что я дал вам то, что вы хотели на этот раз!

Ответ 3

Я сам искал ответ на этот вопрос, и я придумал альтернативный подход. Я просто подумал, что поделюсь им - надеюсь, что это поможет кому-то!:)

# 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