Oracle

Exécuter un ordre DDL dans un bloc PL/SQL

Au sein de votre bloc PL/SQl, vous souhaitez exécuter un ordre DDL (CREATE, DROP, ...).

Par exemple, pour supprimer une table, vous procèderiez ainsi:

DECLARE
  • variables

BEGIN

  • traitements

 

  • ordre DDL
DROP TABLE ma_table;

 

  • autres traitements

END; /

Cependant, à l'exécution de votre bloc, vous obtenez l'erreur suivante:

PLS-00103: Symbole "DROP" rencontré à la place d'un des symboles suivants :

 

begin case declare exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge <a single-quoted SQL string> pipe

Oracle n'est pas capable de traiter un ordre DDL de cette manière au sein d'un bloc PL/SQL. Pour contourner cette limitation, il faut passer par l'utilisation de SQL dynamique ! Ceci se fait via la commande EXECUTE IMMEDIATE. Ainsi, si l'on applique cette technique à l'exemple ci-dessus, cela donnerait:

DECLARE
  requeteDDL VARCHAR2(255);

 

  • autres variables

BEGIN

  • traitements

 

  • ordre DDL
requeteDDL := 'DROP TABLE ma_table';

 

  • exécution de l'ordre DDL
EXECUTE IMMEDIATE requeteDDL;

 

  • autres traitements

END; /

Oracle, SQL et LIMIT

Sous Oracle, le langage SQL ne comporte pas la fonction LIMIT, bien utile pour paginer les résultats d'une requête SELECT. Afin de pallier à cette absence, il est possible d'utiliser la pseudo-colonne ROWNUM.

Ainsi, pour ne prendre en compte que la 1ère ligne de résultats d'un SELECT:

SELECT <colonne_1>, &lt;colonne_n&gt;

FROM <table_1>, &lt;table_n&gt; WHERE <conditions> AND ROWNUM = 1;

Si vous souhaitez les résultats à partir de la 10ème ligne jusqu'à la 20ème:

SELECT <colonne_1>, &lt;colonne_n&gt;

FROM <table_1>, &lt;table_n&gt; WHERE <conditions> AND ROWNUM BETWEEN 10 AND 20;

Vous l'aurez compris, cette colonne s'utilise comme n'importe quelle autre colonne de type numérique, il est donc possible d'y appliquer tous les opérateurs habituels (=, <, >, =<, >=, BETWEEN, ...)

PL/SQL, ne pas interrompre les traitements à la suite d'une exception

Vous avez codé un bloc PL/SQL qui génère une erreur (et oui, ça arrive, même aux meilleurs d'entre nous ;) ) et s'interrompt brutalement à la suite de ce problème... Vous souhaiteriez que l'exécution continue malgré les erreurs survenant en cours de traitement ? Il vous "suffit" d'encapsuler le code susceptible de lever une exception dans un sous-bloc PL/SQL !

Un bloc PL/SQL basique a la structure suivante:

DECLARE
  • variables

BEGIN

  • traitements

 

  • traitement susceptible de générer une erreur

 

  • autres traitements

EXCEPTION

WHEN <exception1> THEN <traitement_exception1>; WHEN <exception2> THEN <traitement_exception2>; WHEN OTHERS THEN <traitement_autres_exceptions>;

END; /

L'encapsulation des traitements problématiques se fait de la manière suivante:

DECLARE
  • variables

BEGIN

  • traitements

 

  • Sous-bloc PL/SQL encapsulant le traitement problématique
DECLARE
  • variables spécifiques
BEGIN
  • traitement susceptible de générer une erreur
EXCEPTION WHEN <exception1> THEN <traitement_exception1>; WHEN <exception2> THEN <traitement_exception2>; WHEN OTHERS THEN <traitement_autres_exceptions>; END;

 

  • autres traitements

END; /

Ainsi, l'erreur sera gérée mais ne stoppera pas la suite des traitements ! A noter que les variables déclarées dans un bloc sont accessibles dans tous les blocs enfants de ce bloc. Utile si l'on souhaite afficher l'état de certaines variables lors du traitement d'une exception...

PL/SQL, augmenter la taille du tampon pour le package DBMS_OUTPUT

Vous êtes-vous déjà retrouvé avec un bloc PL/SQL utilisant la fonction PUT ou PUT_LINE du package DBMS_OUTPUT et générant une erreur car vos appels à ces fonctions affichent "trop" de données à l'écran ? Pour ne plus avoir à faire à cette vilaine erreur

ORA-20000: Buffer overflow, limit of (buffer_limit) bytes.

il vous faut augmenter la taille du tampon utilisé ! Car oui, dans ce cas-là, la taille compte :p Par défaut, la taille du tampon est fixée à 20000 bytes. Pour augmenter cette valeur, il vous faut passer par la procédure DBMS_OUTPUT.ENABLE. Cette dernière prend en paramètre une valeur numérique représentant la taille en bytes du buffer. Cette valeur doit être comprises entre 2000 et 1000000 bytes.

Ainsi, pour fixer la taille du tampon à 500000 bytes, vous procéderiez ainsi:

SET SERVEROUTPUT ON;

  DECLARE

  • variables

BEGIN

DBMS_OUTPUT.ENABLE(500000);

 

  • traitements

END; /

Oracle, ordres SQL et caractère '&'

Aujourd'hui, en exécutant des requêtes SQL sur une base Oracle 10g, j'ai été confronté à un léger soucis... J'essayais de lancer un UPDATE sur une table dont un des champs est de type VARCHAR2. Je voulais donc mettre à jour ce champ avec une chaîne de caractères contenant le caractère &. Mon ordre SQL intégré dans un bloc PL/SQL ressemblait à ça:

BEGIN

  • traitements ...

 

UPDATE TABLE SET CHAMP = 'blablabla... &nbsp; blablablabla...' WHERE ID = toto;

 

  • traitements ...

END; /

Or à l'exécution de la requête, que ce soit sous SQL Plus ou Toad (ou tout autre client Oracle), Oracle me demande d'entrer une valeur pour nbsp; :/

Ainsi, chaque occurence de &xxx dans un ordre SQL quelconque (SELECT, UPDATE, INSERT, DELETE, ...) est considérée comme un paramètre à passer à la requête. Soit, mais comment faire pour pouvoir utiliser ce caractère comme un caractère normal et non comme un caractère spécial ?

Pour cela, deux solutions:

  • La 1ère consiste à définir le paramètre DEFINE à OFF au début du script SQL. Par exemple:
    SET DEFINE OFF;

  BEGIN

  • traitements ...

 

UPDATE TABLE SET CHAMP = 'blablabla... &nbsp; blablablabla...' WHERE ID = toto;

 

  • traitements ...

END; /

  • La 2ème est d'utiliser la concaténation de chaîne et de couper la chaîne en question juste après chaque occurence de &. En effet, si il est placé en fin de chaîne, Oracle ne l'interprètera pas en tant que caractère spécial ! Ce qui donnerait dans notre cas:
    BEGIN
    
    • traitements ...

 

UPDATE TABLE SET CHAMP = 'blablabla... &' || 'nbsp; blablablabla...' WHERE ID = toto;

 

  • traitements ...

END; /

Cette 2nde solution a l'avantage de ne pas obliger à redéfinir un paramètre qui pourrait avoir des effets de bord ultérieurs...