web-dev-qa-db-fra.com

Fonction sommeil dans ORACLE

J'ai besoin d'exécuter une requête SQL dans Oracle, cela prend un certain temps. J'ai donc écrit cette fonction:

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;

et j'appelle de cette façon

SELECT TEST_SLEEP(10.5) FROM DUAL

mais pour travailler, j'ai besoin d'une allocation de DBMS_LOCK au propriétaire de la procédure.

Comment puis-je réécrire cette fonction sans utiliser le DBMS_LOCK.sleep une fonction?

30
Salvador

À moins d'accorder l'accès à DBMS_LOCK.sleep, cela fonctionnera mais c'est un horrible 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;
41
OMG Ponies

Créez une procédure qui ne fait que votre verrou et installez-le dans un autre utilisateur, qui est "de confiance" avec dbms_lock (USERA), accordez à l'USERA l'accès à dbms_lock.

Accordez ensuite à USERB l'accès à cette fonction. Ils n'auront alors plus besoin d'accéder à DBMS_LOCK

(assurez-vous que vous n'avez pas d'usera et d'utilisateur dans votre système avant d'exécuter cela)

Connectez-vous en tant qu'utilisateur avec des privilèges d'accès pour dbms_lock et pouvez créer des utilisateurs

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 */
22
Matthew Watson

S'il est exécuté dans "sqlplus", vous pouvez exécuter une commande du système d'exploitation hôte "sleep":

!sleep 1

ou

Host sleep 1
7
metatechbe

Depuis Oracle 18c, vous pouvez utiliser la procédure DBMS_SESSION.SLEEP:

Cette procédure suspend la session pendant une période de temps spécifiée.

DBMS_SESSION.SLEEP (seconds  IN NUMBER)

DBMS_SESSION.sleep est disponible pour toutes les sessions sans subventions supplémentaires nécessaires. Veuillez noter que DBMS_LOCK.sleep est obsolète.

Si vous avez besoin d'une mise en veille simple, vous pouvez utiliser WITH FUNCTION:

WITH FUNCTION my_sleep(i NUMBER)
RETURN NUMBER
BEGIN
    DBMS_SESSION.sleep(i);
    RETURN i;
END;
SELECT my_sleep(3) FROM dual;
5
Lukasz Szozda

À propos de Java code enveloppé par une procédure? Simple et fonctionne très bien.

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)';
4
MacTouch

Il y a un bon article sur ce sujet: PL/SQL: Sleep sans utiliser DBMS_LOCK qui m'a aidé. J'ai utilisé l'option 2 enveloppée dans un package personnalisé. Les solutions proposées sont:

Option 1: APEX_UTIL.sleep

Si APEX est installé, vous pouvez utiliser la procédure "PAUSE" du package public APEX_UTIL.

Exemple - "Attendez 5 secondes":

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

Option 2: Java.lang.Thread.sleep

Une autre option est l'utilisation de la méthode "sleep" de la classe Java "Thread", que vous pouvez facilement utiliser en fournissant une procédure d'encapsulation PL/SQL simple:

Remarque: N'oubliez pas que "Thread.sleep" utilise des millisecondes!

--- 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;
/
3
dominik

Il serait préférable de mettre en place un mécanisme de synchronisation. Le plus simple est d'écrire un fichier une fois le premier fichier terminé. Vous avez donc un fichier sentinelle.

Les programmes externes recherchent donc le fichier sentinelle pour exister. Quand il le fait, il sait qu'il peut utiliser en toute sécurité les données du vrai fichier.

Une autre façon de procéder, similaire à la façon dont certains navigateurs le font lors du téléchargement de fichiers, consiste à avoir le fichier nommé base-name_part jusqu'à ce que le fichier soit complètement téléchargé, puis à la fin renommer le fichier en base-name. De cette façon, le programme externe ne peut pas "voir" le fichier tant qu'il n'est pas terminé. De cette façon, il ne serait pas nécessaire de réécrire le programme externe. Ce qui pourrait être le mieux adapté à cette situation.

3
Bozon

Vous pouvez utiliser DBMS_PIPE.SEND_MESSAGE avec un message trop volumineux pour le canal, par exemple pour un délai de 5 secondes, écrivez XXX sur un canal qui ne peut accepter qu'un octet en utilisant un délai de 5 secondes comme ci-dessous

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

Mais cela nécessite une subvention pour DBMS_PIPE alors peut-être pas mieux.

1
The love dada

Vous pouvez utiliser le DBMS_ALERT package comme suit:

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;
1
sealor

Si Java est installé sur votre 11G, vous pouvez le faire dans une classe Java et l'appeler depuis votre PL/SQL, mais je ne suis pas sûr qu'il ne nécessite pas également une autorisation spécifique pour appeler Java.

1

Il semble que la procédure/fonction Java pourrait fonctionner. Mais pourquoi ne compilez-vous pas votre fonction sous un utilisateur comme le schéma d'application ou un compte administrateur qui a cette autorisation et accordez simplement votre compte développeur à exécuter sur De cette façon, les droits définitifs sont utilisés.

1
Kuberchaun