2011-01-15 10 views

Respuesta

16

que puede usar:

Buyer.update_all("position = position + 1", ["position >= ?", n]) 

Esto generaría la consulta, si n = 25:

UPDATE "buyers" SET position = position + 1 WHERE (position >= 25) 

Editar:

Ser que tiene limitaciones de base de datos única, tiene una par de opciones. Para ambas opciones, recomiendo ejecutarlas en una transacción. En primer lugar, puede actualizar cada campo individualmente en orden inverso, pero esto hará que tenga consultas N + 1. Para un pequeño conjunto de datos, esto no será un problema, pero para un conjunto de datos más grande, esto podría afectar el rendimiento.

Buyer.transaction do 
    Buyer.select("id, position").where(["position >= ?", n]).order("position DESC").each do |buyer| 
     buyer.position += 1 
     buyer.save 
    end 
end 

La otra opción, para evitar N + 1 consultas, es cambiar los incrementos de posición a 100 (o 10). Esto le permitirá actualizar las posiciones en dos consultas, en lugar de N + 1. Entonces, en lugar de tener las posiciones 1, 2, 3, etc., tendría 100, 200, 300, etc. Luego, para hacer una actualización, incrementaría todos los valores en 101, y luego seguiría la actualización con una actualización para restar el 1.

Buyer.transaction do 
    Buyer.where(["position >= ?", n]).scoping do 
     Buyer.update_all("position = position + 101") 
     Buyer.update_all("position = position - 1") 
    end 
end 
+0

¿En qué orden se actualizará la 'posición's? Si, por ejemplo, hay 10 compradores con 'posiciones 'de 1 a 10, respectivamente, y n = 6, me gustaría que primero 10 se convierta en 11, luego 9 en 10, luego 8 en 9, y así sucesivamente. La 'posición 'es' ÚNICA ', así que no puedo tener dos posiciones iguales en ningún momento. –

+0

Si está utilizando validaciones de Rails, no se encontrará con errores de validación con este método. –

+0

UNIQUE es una restricción de base de datos en mi caso. –

-1
class Buyer < ActiveRecord::Base 
    scope :positioned_at_or_above, lambda {|pos| where("position >= ?", pos) } 

    def self.increment(amount, position_threshold) 
    Buyer.positioned_at_or_above(position_threshold).each{|buyer| buyer.update_attributes(:position => buyer.position + amount)} 
    end 
end 

-

increment ∴ rails c                                        
Loading development environment (Rails 3.0.3) 
>> Buyer.count 
=> 0 
>> (1..10).each {|idx| Buyer.create(:name => "Buyer ##{idx}", :position => idx)} 
=> 1..10 
>> pp Buyer.all 
[#<Buyer id: 11, name: "Buyer #1", position: 1>, 
#<Buyer id: 12, name: "Buyer #2", position: 2>, 
#<Buyer id: 13, name: "Buyer #3", position: 3>, 
#<Buyer id: 14, name: "Buyer #4", position: 4>, 
#<Buyer id: 15, name: "Buyer #5", position: 5>, 
#<Buyer id: 16, name: "Buyer #6", position: 6>, 
#<Buyer id: 17, name: "Buyer #7", position: 7>, 
#<Buyer id: 18, name: "Buyer #8", position: 8>, 
#<Buyer id: 19, name: "Buyer #9", position: 9>, 
#<Buyer id: 20, name: "Buyer #10", position: 10>] 
=> nil 
>> pp Buyer.positioned_at_or_above(4) 
[#<Buyer id: 14, name: "Buyer #4", position: 4>, #<Buyer id: 15, name: "Buyer #5", position: 5>, #<Buyer id: 16, name: "Buyer #6", position: 6>, #<Buyer id: 17, name: "Buyer #7", position: 7>, #<Buyer id: 18, name: "Buyer #8", position: 8>, #<Buyer id: 19, name: "Buyer #9", position: 9>, #<Buyer id: 20, name: "Buyer #10", position: 10>] 
=> nil 
>> pp Buyer.positioned_at_or_above(4).all 
[#<Buyer id: 14, name: "Buyer #4", position: 4>, 
#<Buyer id: 15, name: "Buyer #5", position: 5>, 
#<Buyer id: 16, name: "Buyer #6", position: 6>, 
#<Buyer id: 17, name: "Buyer #7", position: 7>, 
#<Buyer id: 18, name: "Buyer #8", position: 8>, 
#<Buyer id: 19, name: "Buyer #9", position: 9>, 
#<Buyer id: 20, name: "Buyer #10", position: 10>] 
=> nil 
>> Buyer.increment(1000, 4) 
=> [#<Buyer id: 14, name: "Buyer #4", position: 1004>, #<Buyer id: 15, name: "Buyer #5", position: 1005>, #<Buyer id: 16, name: "Buyer #6", position: 1006>, #<Buyer id: 17, name: "Buyer #7", position: 1007>, #<Buyer id: 18, name: "Buyer #8", position: 1008>, #<Buyer id: 19, name: "Buyer #9", position: 1009>, #<Buyer id: 20, name: "Buyer #10", position: 1010>] 
>> pp Buyer.all 
[#<Buyer id: 11, name: "Buyer #1", position: 1>, 
#<Buyer id: 12, name: "Buyer #2", position: 2>, 
#<Buyer id: 13, name: "Buyer #3", position: 3>, 
#<Buyer id: 14, name: "Buyer #4", position: 1004>, 
#<Buyer id: 15, name: "Buyer #5", position: 1005>, 
#<Buyer id: 16, name: "Buyer #6", position: 1006>, 
#<Buyer id: 17, name: "Buyer #7", position: 1007>, 
#<Buyer id: 18, name: "Buyer #8", position: 1008>, 
#<Buyer id: 19, name: "Buyer #9", position: 1009>, 
#<Buyer id: 20, name: "Buyer #10", position: 1010>] 
=> nil 
>> 
+0

Gran uso de alcances. Sin embargo, ¿su solución de incremento no se ejecutaría en el problema de consultas N + 1 tal como está escrito? –

+1

Sería mejor eliminar las consultas N + 1 para hacer algo como esto: Buyer.positioned_at_or_above (position_threshold) .update_all (["position = position +?", Amount]) –

+0

@Sean: great point. Después de leer su solución anoche hice una versión que parecía Buyer.positioned_at_or_above (position_threshold) .update_all (["position = position +?", Amount]). El resultado fue establecer TODAS las posiciones en cero. ¡Guauu! En ese momento era tarde, así que di un puntapié. –

1

Si esto es ad-hoc, se puede quitar la restricción/índice, ejecute la actualización, y luego volver a añadirla utilizando SQL regulares de edad.

Cuestiones relacionadas