С 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?
Мне нужны / нужны настоящие подзапросы, без обходных путей (с использованием нескольких запросов).
not
условие now . Мне удалось добиться этого в Rails 3, изменив подход в этом посте : вsubquery = Profile.select("user_id").where(gender: 'm')).to_sql; Message.where('user_id NOT IN (#{subquery}))
основном,ActiveRecord
методы используются для создания завершенного, правильно процитированного подзапроса, который затем встроен во внешний запрос. Основным недостатком является то, что параметры подзапроса не связаны.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
... выведет следующий 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)
Искренне надеюсь, что на этот раз дал вам то, что вы хотели!
источник
Я сам искал ответ на этот вопрос и придумал альтернативный подход. Я просто подумал, что поделюсь им - надеюсь, это кому-то поможет! :)
# 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})")
Другая альтернатива:
Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })
источник
Это пример вложенного подзапроса с использованием 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("...") }
источник