Con SQL puedo hacer fácilmente subconsultas como esta
User.where(:id => Account.where(..).select(:user_id))
Esto produce:
SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)
¿Cómo puedo hacer esto usando 3 activerecord / arel / meta_where de rails?
Necesito / quiero subconsultas reales, sin soluciones ruby (usando varias consultas).
ruby-on-rails-3.1
subquery
arel
gucki
fuente
fuente
not
condición . Pude lograrlo en Rails 3 ajustando el enfoque en esta publicación :subquery = Profile.select("user_id").where(gender: 'm')).to_sql; Message.where('user_id NOT IN (#{subquery}))
Básicamente, losActiveRecord
métodos se utilizan para crear la subconsulta completada y citada correctamente, que luego se incluye en la consulta externa. El principal inconveniente es que los parámetros de las subconsultas no están vinculados.Message.where.not(user_id: Profile.select("user_id").where(gender: 'm'))
- que genera una subselección "NOT IN". Acabo de resolver mi problema ..En ARel, los
where()
métodos pueden tomar matrices como argumentos que generarán una consulta "WHERE id IN ...". Entonces, lo que ha escrito está en la línea correcta.Por ejemplo, el siguiente código ARel:
User.where(:id => Order.where(:user_id => 5)).to_sql
... que es equivalente a:
User.where(:id => [5, 1, 2, 3]).to_sql
... generaría el siguiente SQL en una base de datos PostgreSQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)"
Actualización: en respuesta a comentarios
Bien, entendí mal la pregunta. Creo que desea que la subconsulta enumere explícitamente los nombres de columna que se seleccionarán para no llegar a la base de datos con dos consultas (que es lo que hace ActiveRecord en el caso más simple).
Puede utilizar
project
paraselect
en su sub-selección:accounts = Account.arel_table User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))
... que produciría el siguiente SQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)
¡Espero sinceramente haberte dado lo que querías esta vez!
fuente
Yo mismo estaba buscando la respuesta a esta pregunta y se me ocurrió un enfoque alternativo. Pensé en compartirlo, ¡espero que ayude a alguien! :)
# 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})")
¡Bingo! ¡Bango!
Funciona con Rails 3.1
fuente
subquery = Account.where(...).select(:id).to_sql
query = User.where("users.account_id IN (#{subquery})")
Otra alternativa:
Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })
fuente
Este es un ejemplo de una subconsulta anidada que usa rails ActiveRecord y usa JOINs, donde puede agregar cláusulas en cada consulta, así como el resultado:
Puede agregar los ámbitos anidado inner_query y outer_query en su archivo de modelo y usar ...
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("...")
Un ejemplo del alcance:
scope :inner_query , -> (ids) { select("...") .joins("left join users on users.id = accounts.id") .where("users.account_id IN (?)", ids) .group("...") }
fuente