2011-07-26 8 views
10

En momentos como este, la abstracción de Rails a partir de SQL sin procesar me vuelve loco. En MySQL podría hacer esto:Realizando una actualización_toda con combinaciones en Rails

¿Cómo puedo hacer esto en Rails 3.0.1 con la carga ansiosa? He intentado todo lo siguiente:

Tasks.joins(:project).where('projects.organization_id' => 42, :invoice_id => nil).update_all(:invoice_id => 7) 

Y todas las variaciones de las anteriores. Todos dieron errores o no encontraron nada.

Entonces traté de usar scope:

Task.scope :find => {:joins => :project, :conditions => ["projects.organization_id == ? AND invoice_id IS NULL", @organization.id] } do 
    Task.update_all :invoice_id => @invoice.id 
end 

Éste me dio el error undefined method 'to_sym' for #<Hash:0x1065c6438>.

He pasado demasiadas horas en esto, solo para replicar una simple consulta SQL. ¡Por favor ayuda!


EDIT: mala solución temporal para moverse por n + 1:

task_ids = Task.select('tasks.id').joins(:project).where('projects.organization_id' => @organization.id, :invoice_id => nil).collect{|t| t.id} 
Task.update_all ['invoice_id = ?', @invoice.id], ["id in (#{task_ids.join(',')})"] 

Respuesta

3

"ACTUALIZACIÓN DE" no es SQL estándar, por lo que no es sorprendente si no está soportado directamente por Registro activo Sin embargo, Active Record le brinda una forma de eludir sus abstracciones y simplemente emitir SQL directamente, para aquellos momentos en que debe hacer algo que no es compatible. Dentro de un modelo:

sql = "UPDATE FROM tasks AS t 
LEFT JOIN projects as p 
ON t.project_id = p.id 
SET t.invoice_id = 7 
WHERE p.organization_id == 42 
AND t.invoice_id IS NULL" 
connection.update_sql(sql) 

ActiveRecord :: Base también tiene un método "select_by_sql" que permite que sus sentencias de selección no estándar vuelven instancias de modelo registro activos regulares.

+0

Gracias, Wayne. Me gustaría resumir si es posible porque los diferentes sabores de SQL manejan este tipo de consulta de forma muy diferente y me preocupa que se rompa en el futuro. Mi ejemplo anterior podría cambiarse a 'actualizar tareas, proyectos establecidos ...' y todavía tendría problemas. Si tienes más pensamientos, ¡te lo agradecería! – glortho

+0

@Jed, Eso también sería SQL no estándar. No sé cómo Active Record lo resumiría. Algunas cosas que puede hacer con SQL no coinciden con la abstracción CRUD muy bien. Este especialmente. –

+0

Interesante. Entonces, ¿1 + n (carga no ansiosa) realmente es la única forma de hacer esto en la capa de abstracción? No puedo creer que 'scope' no ofrezca de alguna manera una solución. ¿No debería funcionar algo como mi segundo esfuerzo anterior? Solo voy con 1 + n por ahora, pero definitivamente no es una solución sostenible. Gracias por su ayuda ... – glortho

0

creo que la siguiente

UPDATE FROM tasks AS t 
LEFT JOIN projects as p 
ON t.project_id = p.id 
SET t.invoice_id = 7 
WHERE p.organization_id == 42 
AND t.invoice_id IS NULL 

se podría escribir como la siguiente consulta Arel:

Tasks.include(:projects).where("projects.organization_id = ?", 42).where("tasks.invoice_id IS NULL").update_all("tasks.invoice_id = ?", 7) 

Esto supone que tiene la asociación correcta entre tareas y proyectos.

2

Esto es puramente rubí/rieles, que no debe ser etiquetado como SQL -

La única información de SQL se puede conseguir es: inicie desde otro equivalente sintáctica en lugar de la "actualización de" que no es estándar, como por ejemplo esto (que tampoco haría, pero oye, no uso ruby ​​/ rails).

UPDATE tasks t 
SET t.invoice_id=7 
WHERE 
t.invoice_id IS NULL 
AND 
(SELECT 
p.organization_id 
FROM tasks t2 
LEFT JOIN projects p 
ON t.project_id=p.id 
WHERE t2.id=t.id)=42 
3

creo que al menos @ carriles 3.0.8 y 2.0.10 Arel, que no pudo generar directamente ACTUALIZACIÓN DE, pero es posible obtener el mismo resultado con resolviendo la unión como una sub-consulta , p.ej

Task.where(:invoice_id=>nil). 
where(:project_id=>Project.where(:organization_id=>42).collect(&:id)). 
update_all(:invoice_id => 7) 

Esto genera SQL como:

UPDATE "tasks" 
SET "invoice_id" = 7 
WHERE "invoice_id" IS NULL AND "project_id" IN (1,2,3); 
-- assuming projects 1,2,3 have organization_id = 42 

De ourse, que se está resolviendo la sub-consulta en los carriles no en SQL.Para generar un sub-select en SQL, se pueden mezclar en un poco de Arel así:

t = Task.arel_table 
p = Project.arel_table 
Task.where(:invoice_id=>nil). 
where(
    t[:project_id].in(
    p.where(
     p[:organization_id].eq(42) 
    ).project(p[:id]) 
) 
).update_all(:invoice_id => 7) 

que genera SQL como esto:

UPDATE "tasks" 
SET "invoice_id" = 7 
WHERE "invoice_id" IS NULL 
AND "project_id" IN (
SELECT "projects"."id" FROM "projects" 
WHERE "projects"."organization_id" = 42 
); 

Hay una manera Arel pura de hacer esto, pero la La sintaxis de UpdateManager está muy poco documentada

Cuestiones relacionadas