DML внутри вызова функции

У меня есть старое клиентское программное обеспечение с подключенной базой данных оракула для сохранения. В качестве интерфейса клиентское ПО допускает только вызов функций и процедур. У меня почти полный доступ к базе данных, т. е. я могу определять функции и процедуры. Из-за интерфейса только функции могут возвращать значения, и я не могу использовать параметр OUT для процедур.

Теперь я просто хочу прочитать значение из таблицы:

SELECT value FROM myTable WHERE id = 42;

И затем увеличьте значение:

UPDATE myTable SET value = value + 1 WHERE id = 42;

Я мог бы использовать функцию для оператора select и процедуру для обновления и вызывать их последовательно. Проблема здесь в отсутствии транзакций на стороне клиента. Таким образом, между select и update другой поток может получить неправильные значения.

Итак, мой вопрос: как я могу использовать оба вызова в транзакции без использования транзакций...

Испробованные подходы:

  • Используйте анонимные блоки PL/SQL -> синтаксис не поддерживается клиентом.
  • Поместите оба вызова в одну функцию -> DML не разрешен в операторе выбора.
  • PRAGMA AUTONOMOUS_TRANSACTION -> Я слышал, что это плохо, и его нельзя использовать.

person Thanthla    schedule 18.12.2017    source источник
comment
Functions предназначены для не побочных эффектов. Если вы хотите изменить состояние базы данных, вам нужно использовать файл Procedure.   -  person MatBailie    schedule 18.12.2017
comment
Если вам нужен простой counter, и в этом сценарии больше ничего не происходит, вы можете рассмотреть CREATE SEQUENCE?   -  person MatBailie    schedule 18.12.2017
comment
Если я использую процедуру (и не могу использовать OUT), мне нужны два вызова базы данных, которые не сохраняют потоки.   -  person Thanthla    schedule 18.12.2017
comment
Затем вам нужно что-то вроде SEQUENCE (если у вас только один счетчик, а не таблица, полная счетчиков), или кто-то должен исправить архитектуру вашего программного обеспечения. Вы перечислили все вещи, которые вы не можете использовать (транзакции, процедуры и т. д.), и таким образом лишили базу данных возможности делать то, что вы хотите. Если вы не можете использовать то, что должны использовать, устраните проблему, мешающую вам использовать правильные инструменты.   -  person MatBailie    schedule 18.12.2017
comment
@MatBailie не уверен, что сквенс поможет. Я думаю, я не могу обновить его и в функции? Однако должен сработать триггер, увеличивающий значение после выбора?   -  person Thanthla    schedule 18.12.2017
comment
Не существует такой вещи, как триггер AFTER SELECT.   -  person MatBailie    schedule 18.12.2017
comment
Я не могу исправить коммерческое программное обеспечение, которое не является открытым исходным кодом...   -  person Thanthla    schedule 18.12.2017
comment
Я перефразирую вашу ситуацию: I have software that means I can't use any of the intended database constructs to atomically query and amend data. Is there some way that I can hack around and use the wrong database tools for the job, but it still be as robust as using the right tools? Короче говоря, у вас есть проблема с архитектурой: либо программное обеспечение не соответствует вашим требованиям (замените его), либо оно не было настроено / использовано неправильно для этого варианта использования.   -  person MatBailie    schedule 18.12.2017
comment
Это говорит о том, что запрос последовательности из пользовательской функции работает... community.oracle. com/thread/3905460?start=15&tstart=2   -  person MatBailie    schedule 18.12.2017
comment
@MatBailie Спасибо, теперь я понимаю, куда вы хотите позвонить. Кстати, как вы делаете этот оператор выбора, если ваше программное обеспечение может вызывать только функции и процедуры?   -  person Dmitriy    schedule 18.12.2017
comment
Просто в функции, как ваш удаленный ответ.   -  person Thanthla    schedule 18.12.2017
comment
@Thanthla - Вы пробовали использовать SEQUENCE? Ссылка на community.oracle, которую я предоставил, подразумевает, что она может работать по желанию.   -  person MatBailie    schedule 18.12.2017
comment
@Thanthla В функции, как в моем удаленном ответе, вы можете просто сделать выбор, а затем сделать отдельное обновление. Если вы выбираете несколько строк, то обновите несколько строк. Это все. Могу вернуть и уточнить, если хотите.   -  person Dmitriy    schedule 18.12.2017
comment
@MatBailie Я просмотрел последовательности, и это должно работать для одного значения. Однако я обновил свой вопрос. В таблице несколько строк, поэтому мне понадобится последовательность для каждого кортежа.   -  person Thanthla    schedule 18.12.2017
comment
@Dmitry - Вы можете сделать это в процедуре, но функция не может выполнить оператор UPDATE. OP хочет атомарно прочитать значение, а затем увеличить его без использования функций (потому что вы не можете выполнить ОБНОВЛЕНИЕ в функции), процедур (поскольку программное обеспечение OP не может считывать результаты ), анонимный блок PL/SQL (поскольку программное обеспечение OP не разрешает их) или транзакции (поскольку программное обеспечение OP не разрешает их)...   -  person MatBailie    schedule 18.12.2017


Ответы (1)


Вы можете выполнять DML внутри функции, как показано ниже, но я подчеркиваю — обратите внимание на другие комментарии. Посмотрите на использование последовательности (даже нескольких последовательностей), потому что выполнение DML внутри функции, как правило, плохая идея, поскольку количество выполнений вызова функции (если она вызывается из SQL) не является детерминированным. Кроме того, при использовании в больших объемах возникают проблемы с масштабируемостью. А в многопользовательской среде вам необходимо обрабатывать блокировку/сериализацию, иначе несколько сеансов будут возвращать одно и то же целочисленное значение.

Итак... после всего этого вы все еще хотите идти по этому пути :-(

SQL> create table t ( x int );

Table created.

SQL> insert into t values (0);

1 row created.

SQL>
SQL> create or replace
  2  function f return int  is
  3    pragma autonomous_transaction;
  4    retval int;
  5  begin
  6    update t
  7    set x = x + 1
  8    returning x into retval;
  9    commit;
 10    return retval;
 11  end;
 12  /

Function created.

SQL>
SQL> select f from dual;

         F
----------
         1

1 row selected.

SQL> select * from t;

         X
----------
         1

1 row selected.

SQL> select f from dual;

         F
----------
         2

1 row selected.

SQL> select * from t;

         X
----------
         2

1 row selected.

SQL> select f from dual;

         F
----------
         3

1 row selected.

SQL> select * from t;

         X
----------
         3

1 row selected.
person Connor McDonald    schedule 19.12.2017
comment
Спасибо за ваш ответ. Я знал предложенное вами решение и связанные с ним проблемы. Таким образом, я скорее всего отброшу консистентность и выполню функцию чтения и процедуру обновления. Вероятность того, что два потока будут выполнять этот рабочий процесс с чередованием, настолько мала, что я могу рискнуть. - person Thanthla; 19.12.2017