【问题标题】:Sleep function in ORACLEORACLE 中的睡眠功能
【发布时间】:2011-02-03 10:28:50
【问题描述】:

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

【问题讨论】:

  • 愚蠢的问题,但是这种事情在数据库上的预期(或必要性)行为是什么?你什么时候需要那个?
  • 仅供参考。您的异常块毫无意义。块中的 RETURN 1 永远不会到达,如果块完全丢失......无论如何都会引发异常。
  • 有一次我需要睡眠功能。在这个应用程序中。外部程序调用将数据写入文件的数据库函数。然后外部程序访问该文件。在一个特定的系统上,我们需要在函数结束时休眠,否则外部程序会在文件完全写入之前尝试访问文件。在大多数系统上,它没有任何内置延迟。
  • 所以外部程序没有等待函数返回 - 在这种情况下睡眠可能没有帮助?或者函数在完成写入文件之前返回?如果是后者,那么它是否没有刷新数据或显式关闭文件 - 我无法理解这是如何发生的,否则我可能遗漏了一些东西......
  • 为什么不想使用 DBMS_LOCK.SLEEP() 函数?如此多的数据库应用程序被无效功能的不必要的重新发明所破坏。

标签: sql oracle plsql oracle11g


【解决方案1】:

没有授予对 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;

【讨论】:

  • @Salvador:可能会有更好的答案 - 这个可行,但我不希望表现如此出色。
  • 我同意 vincent.. 这会奏效,但这是一个可怕的可怕想法。
  • equal 是危险的,你可能会遇到跳过值的问题。你应该使用
  • 精氨酸!!!我的天啊!!!这可能会降低整个数据库的性能,因为它会消耗大量的 CPU!!!多亏了这样的“解决方案”,我们在庞大的企业环境中遇到了问题。我不推荐。事实上,敲诈你的数据库管理员来获得这个授权更容易:嘿,或者你给我这个授权,否则我将使用无限循环......你想要什么。这肯定会奏效。
  • 只是为了详细说明:我试图在这里创建一个错误的 PROC - 用于测试目的。
【解决方案2】:

创建一个只执行锁定的过程并将其安装到另一个用户,该用户对 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 */

【讨论】:

  • 这绝对是正确的做法。除了向USERA授予资源和创建会话;此帐户不需要任何特权。
【解决方案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;

【讨论】:

    【解决方案4】:

    有一篇关于这个主题的好文章:PL/SQL: Sleep without using 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

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

    【讨论】:

    • 选项 2 对我不起作用 - ORA-29548: Java system class reported: could not identify release specified in classes.bin
    【解决方案5】:

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

    !sleep 1
    

    或

    host sleep 1
    

    【讨论】:

    【解决方案6】:

    过程包装的 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)';
    

    【讨论】:

      【解决方案7】:

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

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

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

        • 不知道为什么这被否决了 - 这对我来说似乎是一个不错的建议。
        • @monojohnny 可能是因为获得创建 Java 函数的权限并不比获得DBMS_LOCK.SLEEP 的权限更容易。当然,这在理论上是可能的,但几乎可以肯定是不切实际的。还有另一个答案说同样的话,只有实际的示例代码。
        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

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

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

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

          【讨论】:

            【解决方案11】:

            您可以按如下方式使用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;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-04-12
              • 2011-04-08
              • 2014-12-14
              • 1970-01-01
              相关资源
              最近更新 更多