Функция сна в ORACLE - PullRequest
       13

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

27 голосов
/ 01 апреля 2010

Мне нужно выполнить 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?

Ответы [ 10 ]

40 голосов
/ 01 апреля 2010

За исключением предоставления доступа к 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;
21 голосов
/ 02 апреля 2010

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

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

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

Подключитесь как пользователь с привилегиями привилегий для 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 */
7 голосов
/ 08 июля 2013

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

!sleep 1

или

host sleep 1
4 голосов
/ 23 октября 2017

А как насчет кода 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)';
3 голосов
/ 03 марта 2018

Из Oracle 18c можно использовать DBMS_SESSION.SLEEP процедура:

Эта процедура приостанавливает сеанс на указанный период времени.

DBMS_SESSION.SLEEP (seconds  IN NUMBER)

DBMS_SESSION.sleep доступно для всех сессий без дополнительных грантов. Обратите внимание, что DBMS_LOCK.sleep устарела.

Если вам нужен простой запрос сна, вы можете использовать WITH FUNCTION:

WITH FUNCTION my_sleep(i NUMBER)
RETURN NUMBER
BEGIN
    DBMS_SESSION.sleep(i);
    RETURN i;
END;
SELECT my_sleep(3) FROM dual;
3 голосов
/ 26 февраля 2014

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

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

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

1 голос
/ 05 декабря 2017

На эту тему есть хорошая статья: 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;
/
1 голос
/ 09 января 2013

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

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

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

1 голос
/ 01 апреля 2010

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

1 голос
/ 01 апреля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...