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?
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;
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 */
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
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)';
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.
à 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;
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.
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.
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.
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;
/