Oracle SQL - сделайте транзакцию атомарной - PullRequest
3 голосов
/ 26 октября 2011

У меня есть процедура, которая выглядит следующим образом:

create or replace
PROCEDURE NEWJOBIDPROC (JOB_ID OUT NUMBER )
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
-- PROCEDURE TO RETRIEVE THE JOB ID
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
IS
BEGIN
  -- select the job_id
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;

END;

Теперь мой вопрос заключается в следующем.

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

Теперь они оба обновят job_table с тем же значением 200 - что не то, что я хочу.Я не хочу дубликатов.

Итак, как мне пометить весь код как атомарный?Я хочу, чтобы select и обновление выполнялись одновременно и были безопасны для потоков.Я хочу, чтобы два утверждения вместе были помечены как атомарные.

Ответы [ 3 ]

8 голосов
/ 26 октября 2011

См. Ссылку @derobert, предоставленную для получения дополнительной информации, но для вашего конкретного примера вы можете сделать это:

UPDATE JOB_TABLE SET VALUE = VALUE +1 WHERE ID = 50
 RETURNING VALUE INTO JOB_ID;

Но - не рассматривали ли вы вместо этого использование последовательности?

2 голосов
/ 26 октября 2011

Вам следует заблокировать строку, если вы хотите обновить ее позже.Вторая транзакция будет либо заблокирована и будет ждать завершения транзакции (случай по умолчанию), либо получит сообщение об ошибке, если она попыталась заблокировать строку с указанным значением NOWAIT.

BEGIN
  -- select the job_id and LOCK the row so that noone else can modify it
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50 FOR UPDATE NOWAIT;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;    
END;

В любом случае блокировкастрока предотвращает описанное вами «потерянное обновление» .

1 голос
/ 06 октября 2015

Хотя последовательность, вероятно, является лучшим решением для вашей проблемы, я бы использовал оптимистическую блокировку для подобных проблем.Когда вы добавляете столбец отметки времени в JOB_TABLE, вы можете получить отметку времени в запросе выбора и добавить ее в качестве противопоставления предложению where запроса на обновление.

...