@Sean: отличный момент. Прочитав ваше решение вчера вечером, я сделал версию, которая выглядела как Buyer.positioned_at_or_above (position_threshold) .update_all (["position = position +?", Amount]). Результатом было установить ВСЕ позиции на ноль. Вау! В тот момент было поздно, поэтому я наказал.

Buyerль имеет два поля:

имя (строка)позиция (целое число)

Я хотел бы увеличитьposition всех покупателей, чьиposition >= N.

Какой самый простой способ сделать это?

Можно ли добиться этого, используя только один запрос?

 Misha Moroshko15 янв. 2011 г., 06:03
как метод в приложении
 George Anderson15 янв. 2011 г., 06:02
В консоли или метод в приложении? Это одноразовое исправление?

Ответы на вопрос(3)

Решение Вопроса

Вы можете использовать:

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

Это сгенерирует запрос, если n = 25:

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

Редактировать:

Поскольку у вас есть уникальные ограничения базы данных, у вас есть несколько вариантов. Для обоих вариантов я рекомендую запустить их в транзакции. Во-первых, вы можете обновить каждое поле отдельно в обратном порядке, но это приведет к тому, что у вас будет N + 1 запросов. Для небольшого набора данных это не будет проблемой, но для большого набора данных это может повлиять на производительность.

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

Другой вариант, чтобы избежать N + 1 запросов, - изменить приращение позиции на 100 (или 10). Это позволит вам обновить позиции в двух запросах, а не N + 1. Таким образом, вместо позиций 1, 2, 3 и т. Д. У вас будет 100, 200, 300 и т. Д. Затем, чтобы выполнить обновление, вы должны увеличить все значения на 101, а затем выполнить обновление с обновлением, чтобы вычесть 1.

Buyer.transaction do
   Buyer.where(["position >= ?", n]).scoping do
      Buyer.update_all("position = position + 101")
      Buyer.update_all("position = position - 1")
   end
end
 Sean Hill15 янв. 2011 г., 15:27
Если вы используете валидацию Rails, вы не столкнетесь с ошибками валидации при использовании этого метода.
 Misha Moroshko16 янв. 2011 г., 13:31
UNIQUE - это ограничение базы данных в моем случае.
 Misha Moroshko15 янв. 2011 г., 07:23
В каком порядкеpositionбудут обновлены? Если, например, есть 10 покупателей сpositionс 1 по 10 соответственно и n = 6, я бы хотел, чтобы сначала 10 стало 11, затем 9 -, 10, затем 8 - 9 и так далее.position являетсяUNIQUE поэтому я не могу иметь две равные позиции в любое время.
 Sean Hill16 янв. 2011 г., 17:08
Пожалуйста, смотрите мой обновленный ответ.
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..3)
>> Buyer.count
=> 
>> (1..1).each {|idx| Buyer.create(:name => "Buyer ##{idx}", :position => idx)}
=> 1..1
>> 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: 2, name: "Buyer #1", position: 1>]
=> 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: 2, name: "Buyer #1", position: 1>]
=> 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: 2, name: "Buyer #1", position: 1>]
=> nil
>> Buyer.increment(1, 4)
=> [#<Buyer id: 14, name: "Buyer #4", position: 14>, #<Buyer id: 15, name: "Buyer #5", position: 15>, #<Buyer id: 16, name: "Buyer #6", position: 16>, #<Buyer id: 17, name: "Buyer #7", position: 17>, #<Buyer id: 18, name: "Buyer #8", position: 18>, #<Buyer id: 19, name: "Buyer #9", position: 19>, #<Buyer id: 2, name: "Buyer #1", position: 11>]
>> 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: 14>,
 #<Buyer id: 15, name: "Buyer #5", position: 15>,
 #<Buyer id: 16, name: "Buyer #6", position: 16>,
 #<Buyer id: 17, name: "Buyer #7", position: 17>,
 #<Buyer id: 18, name: "Buyer #8", position: 18>,
 #<Buyer id: 19, name: "Buyer #9", position: 19>,
 #<Buyer id: 2, name: "Buyer #1", position: 11>]
=> nil
>> 
 Sean Hill15 янв. 2011 г., 15:32
Отличное использование прицелов. Однако не будет ли ваше решение для приращения столкнуться с проблемой N + 1 запросов, как написано?
 George Anderson15 янв. 2011 г., 17:25
@Sean: отличный момент. Прочитав ваше решение вчера вечером, я сделал версию, которая выглядела как Buyer.positioned_at_or_above (position_threshold) .update_all (["position = position +?", Amount]). Результатом было установить ВСЕ позиции на ноль. Вау! В тот момент было поздно, поэтому я наказал.
 Sean Hill15 янв. 2011 г., 15:45
Для устранения N + 1 запросов было бы лучше сделать что-то вроде этого: Buyer.positioned_at_or_above (position_threshold) .update_all (["position = position +?", Amount])

вы можете удалить ограничение / индекс, запустить обновление, а затем повторно добавить его, используя обычный старый SQL.

Ваш ответ на вопрос