Подтвердить что ты не робот

Функция сна в ORACLE

Мне нужно выполнить sql-запрос в ORACLE, для этого требуется определенное количество времени.

поэтому я написал эту функцию

CREATE OR REPLACE FUNCTION MYSCHEMA.TEST_SLEEP
(
TIME_  IN  NUMBER
)
RETURN INTEGER IS
 BEGIN
   DBMS_LOCK.sleep(seconds => TIME_);
RETURN 1;
 EXCEPTION
   WHEN OTHERS THEN
   RAISE;
   RETURN 1;
END TEST_SLEEP;

и я вызываю таким образом

SELECT TEST_SLEEP(10.5) FROM DUAL

но для работы мне нужно предоставить грант DBMS_LOCK владельцу процедуры.

как я могу переписать эту функцию без использования функции DBMS_LOCK.sleep?

Спасибо заранее.

4b9b3361

Ответ 1

За исключением предоставления доступа к DBMS_LOCK.sleep, это будет работать, но это ужасный взлом:

IN_TIME INT; --num seconds
v_now DATE;

-- 1) Get the date & time 
SELECT SYSDATE 
  INTO v_now
  FROM DUAL;

-- 2) Loop until the original timestamp plus the amount of seconds <= current date
LOOP
  EXIT WHEN v_now + (IN_TIME * (1/86400)) <= SYSDATE;
END LOOP;

Ответ 2

Создайте процедуру, которая только делает вашу блокировку и устанавливает ее другому пользователю, которому доверяют dbms_lock (USERA), предоставляют доступ USERA к dbms_lock.

Затем просто выделите USERB доступ к этой функции. Тогда им не нужно будет иметь доступ к DBMS_LOCK

(убедитесь, что у вас нет пользователя и пользователя в вашей системе перед запуском)

Подключиться как пользователь с привилегиями priv для dbms_lock и создать пользователей

drop user usera cascade;
drop user userb cascade;
create user usera default tablespace users identified by abc123;
grant create session to usera;
grant resource to usera;
grant execute on dbms_lock to usera;

create user userb default tablespace users identified by abc123;
grant create session to userb;
grant resource to useb

connect usera/abc123;

create or replace function usera.f_sleep( in_time number ) return number is
begin
 dbms_lock.sleep(in_time);
 return 1;
end;
/

grant execute on usera.f_sleep to userb;

connect userb/abc123;

/* About to sleep as userb */
select usera.f_sleep(5) from dual;
/* Finished sleeping as userb */

/* Attempt to access dbms_lock as userb.. Should fail */

begin
  dbms_lock.sleep(5);
end;
/

/* Finished */

Ответ 3

Если выполняется в "sqlplus", вы можете выполнить команду операционной системы хоста "sleep":

!sleep 1

или

host sleep 1

Ответ 4

Как насчет кода Java, завернутого процедурой? Простой и отлично работает.

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED SNOOZE AS
public final class Snooze {
  private Snooze() {
  }
  public static void snooze(Long milliseconds) throws InterruptedException {
      Thread.sleep(milliseconds);
  }
}

CREATE OR REPLACE PROCEDURE SNOOZE(p_Milliseconds IN NUMBER) AS
    LANGUAGE JAVA NAME 'Snooze.snooze(java.lang.Long)';

Ответ 5

Было бы лучше реализовать механизм синхронизации. Самый простой способ - записать файл после завершения первого файла. Итак, у вас есть файл часового.

Таким образом, внешние программы ищут, чтобы файл сторожевого устройства существовал. Когда он это знает, он может безопасно использовать данные в реальном файле.

Другой способ сделать это, который похож на то, как некоторые браузеры делают это при загрузке файлов, состоит в том, чтобы иметь файл с именем base-name_part, пока файл не будет полностью загружен, а затем в конце переименуйте файл в base-name. Таким образом, внешняя программа не может "видеть" файл до его завершения. Для этого не потребуется переписывать внешнюю программу. Что может сделать это лучше всего для этой ситуации.

Ответ 6

Если Java установлен на вашем 11G, вы можете сделать это в классе java и вызвать его из своего PL/SQL, но я не уверен, что для него не требуется также специальный грант для вызова java.

Ответ 7

Кажется, что процедура/функция java может работать. Но почему бы вам не скомпилировать свою функцию под пользователем, как схема приложения или учетная запись администратора, которая имеет этот грант, и просто предоставить вам свою учетную запись разработчика. Таким образом используются определенные права.

Ответ 8

Есть хорошая статья на эту тему: PL/SQL: спящий без использования DBMS_LOCK, который помог мне. Я использовал вариант 2, завернутый в пользовательский пакет. Предлагаемые решения:

Вариант 1: APEX_UTIL.sleep

Если APEX установлен, вы можете использовать процедуру "ПАУЗА" из общедоступного пакета APEX_UTIL.

Пример - "Подождите 5 секунд":

SET SERVEROUTPUT ON ;
BEGIN
    DBMS_OUTPUT.PUT_LINE('Start ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
    APEX_UTIL.PAUSE(5);
    DBMS_OUTPUT.PUT_LINE('End   ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
END;
/

Вариант 2: java.lang.Thread.sleep

Другим вариантом является использование метода "sleep" из класса Java "Thread", который вы можете легко использовать, предоставив простую процедуру обертки PL/SQL:

Примечание. Помните, что "Thread.sleep" использует миллисекунды!

--- create ---
CREATE OR REPLACE PROCEDURE SLEEP (P_MILLI_SECONDS IN NUMBER) 
AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';

--- use ---
SET SERVEROUTPUT ON ;
BEGIN
    DBMS_OUTPUT.PUT_LINE('Start ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
    SLEEP(5 * 1000);
    DBMS_OUTPUT.PUT_LINE('End   ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
END;
/

Ответ 9

Вы можете использовать DBMS_PIPE.SEND_MESSAGE с сообщением, которое слишком велико для канала, например, для 5-секундной задержки записи XXX в канал, который может принимать только один байт, используя 5-секундный тайм-аут, как показано ниже

dbms_pipe.pack_message('XXX');<br>
dummy:=dbms_pipe.send_message('TEST_PIPE', 5, 1);

Но для этого требуется грант для DBMS_PIPE, поэтому, возможно, не лучше.