ORACLE中的睡眠功能

问题描述 投票:26回答:10

我需要在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函数的情况下重写此函数?

sql oracle plsql oracle11g
10个回答
39
投票

如果不允许访问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;

1
投票

有一篇关于这个主题的好文章:PL/SQL: Sleep without using DBMS_LOCK帮助了我。我使用自定义包裹的Option 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

另一个选项是使用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;
/

21
投票

创建一个只执行锁定并将其安装到使用dbms_lock(USERA)“信任”的其他用户的过程,授予USERA访问dbms_lock的权限。

然后只授予USERB访问此功能的权限。然后他们不需要能够访问DBMS_LOCK

(在运行之前,请确保系统中没有usera和userb)

以具有dbms_lock的grant privs的用户身份进行连接,并可以创建用户

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
投票

如果在“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)';

3
投票

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

因此外部程序会查找存在的sentinel文件。当它发生时,它知道它可以安全地使用真实文件中的数据。

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


3
投票

从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;

1
投票

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


1
投票

似乎java过程/函数可以工作。但是,为什么不在应用程序架构或具有此授权的管理员帐户之类的用户下编译您的功能,只需授予您的开发者帐户执行权限。这样就可以使用定义者权利。


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拨款,所以也许没有更好的。

© www.soinside.com 2019 - 2024. All rights reserved.