Я осуществляю рефакторинг процедуру импорта данных корпоративного приложения и столкнулся с отрывком, я хотел бы найти лучшее решение. При импорте данных мы должны создать уникальный объект для каждого набора данных и существует счетчик в поле, которое будет использоваться для присвоения этого идентификатора последовательно. Вы читаете поле, чтобы получить следующий бесплатный идентификатор и увеличить его впоследствии для подготовки в следующий раз.
В данный момент это сделано на двух шагах в исходном приложении, записанном в 'C':
SELECT idnext FROM mytable;
UPDATE mytable SET idnext = idnext + 1;
Очевидно, здесь существует состояние состязания, если несколько процессов делают то же самое.
Править: Важный corequisite: Я не могу коснуться базы данных/определения поля, это исключает последовательность.
Мы переписываем в жемчуге, и я хотел бы сделать то же самое, но лучше. Атомарное решение было бы хорошо. К сожалению, мои навыки SQL ограничены, таким образом, я обращаюсь к коллективной мудрости :-)
В данном конкретном случае последовательность является правильным решением, как уже упоминалось. Но если в какой-то ситуации в будущем необходимо как что-то обновить, так и вернуть значение в одном и том же операторе, то можно воспользоваться выражением RETURNING
:
UPDATE atable SET foo = do_something_with(foo) RETURNING foo INTO ?
Если код вызова - PL/SQL, замените ? на локальную переменную PL/SQL; в противном случае вы можете привязать ее в качестве выходного параметра в своей программе.
Правка: Так как вы упомянули Perl, что-то подобное должно работать (непроверено):
my $sth = $dbh->prepare('UPDATE mytable SET idnext = idnext + 1 returning idnext into ?');
my $idnext;
$sth->bind_param_inout(1, \$idnext, 8);
$sth->execute; # now $idnext should contain the value
См. DBI.
Используйте инструкцию SELECT FOR UPDATE. Он гарантирует взаимоисключающие права на запись:
Почему бы не использовать последовательность ?
Создайте последовательность один раз, используя все, что начнется со значением, которое вы хотите:
CREATE SEQUENCE mysequence
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
Затем в вашем приложении при выполнении вы можете использовать это оператор для получения следующего значения:
SELECT mysequence.NEXTVAL
INTO idnext
FROM DUAL;
Обновление: Использование последовательности будет предпочтительным методом, но поскольку вы не можете изменить базу данных, я согласен, что используя возврат должен работать на вашу ситуацию:
UPDATE mytable
SET idnext = idnext + 1
RETURNING idnext
INTO mylocalvariable;
A последовательность будет выполнять работу, посмотреть на E.G. Oracle Sequences