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