40

我需要在 ORACLE 中执行 SQL 查询,这需要一定的时间。所以我写了这个函数:

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函数?

4

11 回答 11

53

如果不授予对 的访问权限DBMS_LOCK.sleep,这将起作用,但这是一个可怕的 hack:

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;
于 2010-04-01T16:05:36.847 回答
22

创建一个仅执行锁定的过程并将其安装到另一个用户,该用户对 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 */
于 2010-04-02T01:09:10.730 回答
16

在 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;
于 2018-03-02T21:18:35.917 回答
10

关于这个主题有一篇很好的文章:PL/SQL: Sleep without using DBMS_LOCK帮助了我。我使用了包裹在自定义包中的选项 2。建议的解决方案是:

选项 1:APEX_UTIL.sleep

如果安装了 APEX,您可以使用公开可用包 APEX_UTIL 中的过程“PAUSE”。

示例——“等待 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

另一种选择是使用 Java 类“Thread”中的“sleep”方法,您可以通过提供一个简单的 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;
/
于 2017-12-05T15:22:17.980 回答
8

如果在“sqlplus”中执行,您可以执行主机操作系统命令“sleep”:

!sleep 1

或者

host sleep 1
于 2013-07-08T09:24:22.557 回答
5

由过程包装的 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)';
于 2017-10-23T14:03:55.920 回答
3

最好实现一个同步机制。最简单的方法是在第一个文件完成后写入文件。所以你有一个哨兵文件。

所以外部程序会寻找存在的哨兵文件。当它知道它可以安全地使用真实文件中的数据时。

另一种方法,类似于某些浏览器在下载文件时的做法,是将文件命名为 base-name_part 直到文件完全下载,然后最后将文件重命名为 base-name。这样,外部程序在完成之前无法“看到”文件。这种方式不需要重写外部程序。这可能最适合这种情况。

于 2014-02-25T22:00:23.647 回答
1

如果您的 11G 上安装了 Java,那么您可以在 java 类中执行它并从您的 PL/SQL 中调用它,但我不确定它是否也不需要特定的授权来调用 java。

于 2010-04-01T15:53:19.527 回答
1

似乎java过程/函数可以工作。但是,您为什么不在应用程序模式或具有此授权的管理员帐户之类的用户下编译您的函数,并仅授予您的开发人员帐户在其上执行。这样就使用了定义者权限。

于 2010-04-01T16:03:03.153 回答
1

您可以使用DBMS_PIPE.SEND_MESSAGE对于管道来说太大的消息,例如延迟 5 秒,将 XXX 写入只能接受一个字节的管道,使用 5 秒超时,如下所示

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

但这需要拨款,DBMS_PIPE所以也许没有更好的办法。

于 2013-01-08T20:29:56.967 回答
1

您可以DBMS_ALERT按如下方式使用该软件包:

CREATE OR REPLACE FUNCTION sleep(seconds IN NUMBER) RETURN NUMBER
AS
    PRAGMA AUTONOMOUS_TRANSACTION;
    message VARCHAR2(200);
    status  INTEGER;
BEGIN
    DBMS_ALERT.WAITONE('noname', message, status, seconds);
    ROLLBACK;
    RETURN seconds;
END;
SELECT sleep(3) FROM dual;
于 2019-08-02T14:14:51.987 回答