sql - requisitos - sociedad limitada ejemplos
¿Consulta de Rails a través de asociación limitada al registro más reciente? (6)
Debería usar los ámbitos aquí: harán su vida mucho más simple (este es un ejemplo de rails3)
En tu modelo book.rb
scope :completed, where("complete = ?", true)
scope :recent, order("created_on ASC")
Luego puede llamar a User.books.recent.completed
para obtener lo que desea
class User
has_many :books
Necesito una consulta que devuelva:
Usuarios cuyo libro más reciente tiene: complete => true. es decir, si el libro más reciente de un usuario tiene: completo => falso, no los quiero en mi resultado .
Lo que tengo hasta ahora
User.joins(:books).merge(Book.where(:complete => true))
que es un comienzo prometedor pero no me da el resultado que necesito. He intentado agregar un .order("created_on desc").limit(1)
hasta el final de la consulta anterior, pero luego obtengo solo un resultado cuando estoy esperando muchos.
¡Gracias!
Esto agrega un poco de sobrecarga, pero ahorra complejidad y aumenta la velocidad más adelante cuando importa.
Agregue una columna "most_recent" a los libros. Asegúrese de agregar un índice.
class AddMostRecentToBooks < ActiveRecord::Migration
def self.change
add_column :books, :most_recent, :boolean, :default => false, :null => false
end
add_index :books, :most_recent, where: :most_recent # partial index
end
Luego, cuando guarde un libro, actualice most_recent
class Book < ActiveRecord::Base
on_save :mark_most_recent
def mark_most_recent
user.books.order(:created_at => :desc).offset(1).update_all(:most_recent => false)
user.books.order(:created_at => :desc).limit(1).update_all(:most_recent => true)
end
end
Ahora, para su consulta
class User < ActiveRecord::Base
# Could also include and preload most-recent book this way for lists if you wanted
has_one :most_recent_book, -> { where(:most_recent => true) }, :class_name => ''Book''
scope :last_book_completed, -> { joins(:books).where(:books => { :most_recent => true, :complete => true })
end
Esto le permite escribirlo de esta manera y el resultado es una relación que se utilizará con otros ámbitos.
User.last_book_completed
No puedo pensar en una forma de hacerlo en una sola consulta, pero puedes hacerlo:
User.all.select { |user| user.books.order("created_at desc").limit(1).complete }
Recientemente me encontré con un problema similar y aquí es cómo lo resolví:
most_recent_book_ids = User.all.map {|user| user.books.last.id }
results = User.joins(:books).where(''books.id in (?) AND books.complete == ?'', most_recent_book_ids, true).uniq
De esta manera solo usamos los métodos ActiveRecord (sin SQL adicional) y podemos reutilizarlos al considerar cualquier subconjunto de libros para los usuarios (primero, último, último n libros, etc.). Necesitas el último ''uniq'' porque de lo contrario cada usuario aparecerá dos veces ...
Si no va a ir con la solución ruby de @ rubyprince, esta es en realidad una consulta de base de datos más compleja de la que ActiveRecord puede manejar en su forma más simple porque requiere una sub consulta. Así es como lo haría completamente con una consulta:
SELECT users.*
FROM users
INNER JOIN books on books.user_id = users.id
WHERE books.created_on = ( SELECT MAX(books.created_on)
FROM books
WHERE books.user_id = users.id)
AND books.complete = true
GROUP BY users.id
Para convertir esto en ActiveRecord haría lo siguiente:
class User
scope :last_book_completed, joins(:books)
.where(''books.created_on = (SELECT MAX(books.created_on) FROM books WHERE books.user_id = users.id)'')
.where(''books.complete = true'')
.group(''users.id'')
end
Luego puede obtener una lista de todos los usuarios que tienen un último libro completado haciendo lo siguiente:
User.last_book_completed
basado en la respuesta de Pan pero con una combinación a la izquierda en lugar de una combinación interna, y sin agrupación:
scope :with_last_book_complete, -> {
subquery = Book.select(:id)
.where("books.user_id = users.id")
.order(created_at: :desc).limit(1)
joins(<<-SQL).where(books: { complete: true })
LEFT JOIN books
ON books.user_id = users.id
AND books.id = (#{subquery.to_sql})
SQL
}