Как я могу выбрать рельсы для обновления на нескольких строках? - PullRequest
0 голосов
/ 09 января 2020

Я не могу понять, как сказать рельсам SELECT ... FOR UPDATE в нескольких рядах. Выглядывая в консоли, Foo.where(bar: "baz").lock выдает правильное значение SQL. Но когда я пытаюсь сделать это в транзакции, этот код ruby фактически не генерирует SQL. Я думаю, что одноразовый работает, потому что консоль rails автоматически запускает .all в отношении для отображения результатов.

Вот что я запускаю в консоли

Foo.transaction do
  Foo.where(bar: "baz").lock  
  Foo.where(bar: "baz").update_all(bar: "baz2")  
end

и SQL:

BEGIN
  Foo Update All (0.5ms)  UPDATE "foos" SET "bar" = $1 WHERE "foos"."bar = $1 [["bar", "baz2"]]
COMMIT

Если я изменю линию блокировки так, чтобы rails думала, что нужно что-то сделать с коллекцией, например, добавить puts в начало или pluck в конец ...

Foo.where(bar: "baz").lock.pluck(:id)

... затем я получаю ожидаемый запрос FOR UPDATE до того, как произойдет обновление, за счет того, что приходится передавать результат по проводам и помещать его в память, а затем ничего с ним не делать.

Таким образом, похоже, что rails правильно использует .lock для изменения SQL отношения, но не существует элегантного способа фактически вызвать блокировку. Я делаю что-то не так или нам нужно использовать какой-то обходной путь (например, pluck) для выполнения запроса?

Ответы [ 2 ]

1 голос
/ 09 января 2020
ApplicationRecord.transaction do
  User.where(id: [1, 2]).lock!.map do |user|
    puts user.id
    user.id
  end
end

производит

   (0.2ms)  BEGIN
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2) FOR UPDATE
1
2
   (0.9ms)  COMMIT
=> [1, 2]
0 голосов
/ 13 января 2020

Вдохновленный обсуждением здесь: https://dba.stackexchange.com/questions/257188/

Я думаю, что лучшим решением общего назначения является

Foo.where(bar: "baz").order(:id).lock.pluck('')

, которое даст

SELECT  FROM "foos" WHERE "foos"."bar" = 'baz' ORDER BY "foos"."id" ASC FOR UPDATE;
...