Функция сна в 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
?
Спасибо заранее.
Ответы
Ответ 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
, поэтому, возможно, не лучше.