La fonction du 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 je l'appelle de cette façon

SELECT TEST_SLEEP(10.5) FROM DUAL

mais pour travailler j'ai besoin de définir subvention de DBMS_LOCK au propriétaire de la procédure.

comment je peux réécrire cette fonction sans utiliser le DBMS_LOCK.sleep fonction?

24
demandé sur Lukasz Szozda 2010-04-01 19:45:39

10 réponses

à moins d'accorder l'accès à DBMS_LOCK.sleep, cela va fonctionner, mais c'est une 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;
37
répondu OMG Ponies 2016-05-04 21:59:06

créer une procédure qui fait juste votre verrouillage et l'installer dans un utilisateur différent, qui est "de confiance" avec dbms_lock ( USERA ), accorder à USERA l'accès à dbms_lock.

alors il suffit de donner à USERB l'accès à cette fonction. Ils n'auront alors pas besoin de pouvoir accéder à DBMS_LOCK

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

se Connecter en tant qu'utilisateur avec grant privs pour dbms_lock, et peut 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 */
20
répondu Matthew Watson 2010-04-02 01:09:10

si exécuté dans "sqlplus", vous pouvez exécuter la commande "sleep" du système d'exploitation hôte :

!sleep 1

ou

host sleep 1
5
répondu metatechbe 2013-07-08 09:24:22

Ce code Java 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
répondu MacTouch 2017-10-23 14:03:55

Il serait préférable de mettre en œuvre un mécanisme de synchronisation. Le plus simple est d'écrire un fichier après le premier dossier est complet. Donc vous avez un dossier sentinelle.

ainsi, les programmes externes recherchent l'existence du fichier sentinelle. Quand il le fait, il sait qu'il peut utiliser en toute sécurité les données dans le fichier réel.

une autre façon de faire cela, qui est similaire à la façon dont certains navigateurs le font lors du téléchargement de fichiers, est d'avoir le fichier nommé base-name_part jusqu'à ce que le fichier est complètement téléchargé et ensuite à la fin renommer le fichier en base-name. De cette façon, le programme externe ne peut pas "voir" le fichier jusqu'à la fin. De cette façon, ne pas exiger la réécriture du programme externe. Ce qui pourrait être mieux pour cette situation.

3
répondu Bozon 2014-02-25 22:00:23

à Partir d'Oracle 18c vous pouvez utiliser DBMS_SESSION.SLEEP procédure:

cette procédure suspend la session pour une période déterminée.

DBMS_SESSION.SLEEP (seconds  IN NUMBER)

DBMS_SESSION.sleep est disponible pour toutes les sessions sans aucune subvention supplémentaire nécessaire. Veuillez noter que DBMS_LOCK.sleep est obsolète.

Si vous avez besoin simple requête de sommeil, 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;
3
répondu Lukasz Szozda 2018-03-02 21:18:35

si Java est installé sur votre 11G alors vous pouvez le faire dans une classe java et l'appeler à partir de votre PL/SQL, mais je ne suis pas sûr qu'il ne nécessite pas aussi une subvention spécifique pour appeler java.

1
répondu Matthieu BROUILLARD 2010-04-01 15:53:19

semble que la procédure/fonction java pourrait fonctionner. Mais pourquoi ne compilez-vous pas votre FONCTION sous un utilisateur tel que le schéma d'application ou un compte administrateur qui dispose de cette subvention et faites exécuter votre compte de développeur sur ce dernier. De cette façon, les droits de définition sont utilisés.

1
répondu Kuberchaun 2010-04-01 16:03:03

Vous pouvez utiliser DBMS_PIPE.SEND_MESSAGE avec un message qui est trop grand pour le tuyau, par exemple pour un retard de 5 secondes écrire XXX à un tuyau qui ne peut accepter un octet à l'aide d'un délai d'attente 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 donc peut-être pas mieux.

1
répondu The love dada 2013-01-08 20:52:09
PL/SQL: dormir sans utiliser DBMS_LOCK qui m'a aidé. J'ai utilisé L'Option 2 enveloppée dans un paquet personnalisé. Les solutions proposées sont les suivantes:

Option 1: APEX_UTIL.sommeil

si APEX est installé, vous pouvez utiliser la procédure "PAUSE" du paquet publiquement disponible 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.Fil.sommeil

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 simple PL/SQL wrapper:

Remarque: n'oubliez pas que "Thread.dormir" utilise 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;
/
1
répondu dominik 2017-12-05 15:22:17