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