2010-11-21 7 views
7

Necesito eliminar muchos registros a la vez y necesito hacerlo en base a una condición en otro modelo que está relacionado por una relación "belongs_to". Sé que puedo recorrer cada comprobación de la condición, pero esto lleva una eternidad con mi gran conjunto de registros porque para cada "belongs_to" hace una consulta por separado.¿Es posible eliminar_todos con condiciones de unión interna?

Aquí hay un ejemplo. Tengo un modelo de "Producto" que "pertenece_ a" un "Artista" y digamos que el artista tiene una propiedad "is_disabled".

Si quiero borrar todos los productos que pertenecen a artistas con discapacidad, me gustaría ser capaz de hacer algo como:

Product.delete_all(:joins => :artist, :conditions => ["artists.is_disabled = ?", true]) 

Es esto posible? He hecho esto directamente en SQL anteriormente, pero no estoy seguro de si es posible hacerlo a través de los rieles.

Respuesta

4

El problema es que delete_all descarta toda la información de unión (y con razón). Lo que quiere hacer es capturar eso como una selección interna.

Si está usando Rails 3 se puede crear un ámbito que le dará lo que quiere:

class Product < ActiveRecord::Base 
    scope :with_disabled_artist, lambda { 
    where("product_id IN (#{select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})") 
    } 
end 

Usted llamada de consulta se convierte entonces en

Product.with_disabled_artist.delete_all 

También puede utilizar la misma consulta en línea, pero eso no es muy elegante (o autodocumentado):

Product.where("product_id IN (#{Product.select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})").delete_all 
+2

¿Cómo es que "con razón" para eliminar las uniones ? Las condiciones de unión a menudo se necesitan para limitar los registros que se eliminarán. Creo que la peor parte es que lo hace en silencio, lo que podría ser bastante sorprendente. –

+0

Solo quiero decir que no puedes hacer un join borrar todo eso en SQL de todos modos, por lo tanto, con razón. Estoy bastante seguro de que las versiones posteriores de Rails hacen subconsultas de todos modos. No estoy seguro de que sea el mejor enfoque, ya que hace que parezca la consulta subyacente en una combinación cuando en realidad es una sub consulta. – gcastro

+1

@gcastro Eso es falso: en mysql, al menos uno puede realizar una eliminación con una instrucción de combinación. http://dev.mysql.com/doc/refman/5.7/en/delete.html – bluefear

0

Si está utilizando Rails 2 y No puedes hacer lo anterior. Una alternativa es usar una cláusula join en un método de búsqueda y eliminar llamada en cada elemento.

TellerLocationWidget.find(:all, :joins => [:widget, :teller_location], 
     :conditions => {:widgets => {:alt_id => params['alt_id']}, 
     :retailer_locations => {:id => @teller_location.id}}).each do |loc| 
     loc.delete 
    end 
+0

Esto dará como resultado eliminaciones individuales que no son iguales y no se escalarán. Debería poder usar ámbitos con nombre en Rails 2. – gcastro

1

En Rails 4 (he probado en 4.2) casi se puede ver cómo OP originalmente quería

Application.joins(:vacancy).where(vacancies: {status: 'draft'}).delete_all 

dará

DELETE FROM `applications` WHERE `applications`.`id` IN (SELECT id FROM (SELECT `applications`.`id` FROM `applications` INNER JOIN `vacancies` ON `vacancies`.`id` = `applications`.`vacancy_id` WHERE `vacancies`.`status` = 'draft') __active_record_temp) 
+0

Si bien esto está bien en PostgreSQL, es letalmente lento en el servidor MySQL. Tener cuidado. – lzap

Cuestiones relacionadas