ORACLE
1 Introduction à PL/SQL
1.1 Généralités
PL/SQL est un acronyme de "Procedural Language/Structured Query Language". PL/SQL est une extension de SQL proposée par Oracle ; c'est donc un langage spécifique de la base de donnnées Oracle . C'est un langage de quatrième génération i.e un langage 4GL(fourth-generation programming language) permettant de faire des traitements avec
le SGBD Oracle
Un programme PL/SQL est structuré en blocs et chaque bloc est constitué d'un ensemble d'instructions :
Un bloc PL/SQL peut être "externe" et dans ce cas on parle de bloc anonyme, ou alors stocké dans la base de données sous forme de procédure, de fonction ou de trigger. Un bloc PL/SQL est intégralement envoyé au moteur PL/SQL, qui traite chaque instruction PL/SQL et sous-traite les instructions purement SQL au moteur SQL, afin de réduire le trafic réseau (pour les application ne résident pas sur la même machine que la base de données ).
Chaque bloc PL/SQL est constitué d'au plus 3 sections :
1 Une section facultative de déclaration et initialisation de types, variables et constantes
2 Une section obligatoire contenant les instructions d'exécution
3 Une section facultative de gestion des erreurs
PL/SQL est un acronyme de "Procedural Language/Structured Query Language". PL/SQL est une extension de SQL proposée par Oracle ; c'est donc un langage spécifique de la base de donnnées Oracle . C'est un langage de quatrième génération i.e un langage 4GL(fourth-generation programming language) permettant de faire des traitements avec
figure 1.0 |
Un programme PL/SQL est structuré en blocs et chaque bloc est constitué d'un ensemble d'instructions :
Un bloc PL/SQL peut être "externe" et dans ce cas on parle de bloc anonyme, ou alors stocké dans la base de données sous forme de procédure, de fonction ou de trigger. Un bloc PL/SQL est intégralement envoyé au moteur PL/SQL, qui traite chaque instruction PL/SQL et sous-traite les instructions purement SQL au moteur SQL, afin de réduire le trafic réseau (pour les application ne résident pas sur la même machine que la base de données ).
Chaque bloc PL/SQL est constitué d'au plus 3 sections :
1 Une section facultative de déclaration et initialisation de types, variables et constantes
2 Une section obligatoire contenant les instructions d'exécution
3 Une section facultative de gestion des erreurs
Pour Illustrer notre propos voici la forme d' un bloc type de PL/SQL:
Un bloc PL/SQL minimum peut-être représenté de la façon suivante :
[DECLARE ... déclarations et initialisation] BEGIN ... instructions exécutables [EXCEPTION ... interception des erreurs] END;
BEGIN Null ; END ;
6 Types de données scalaires
Généralités
PL/SQL posssède un certain nombre de types scalaires ( on appelle type scalaires en informatique les types non composé comme les structures ou les Unions en C/C++); Ces types de données peuvent être soient :
PL/SQL posssède un certain nombre de types scalaires ( on appelle type scalaires en informatique les types non composé comme les structures ou les Unions en C/C++); Ces types de données peuvent être soient :
1 Booléen 2 Date/heure 3 caractère 4 Nombre
6.1 Booléen
Le type de données BOOLEAN (ne prend pas de paramétres voir détatille après), est utilisé pour stocker une valeur binaire, TRUE ou FALSE. Ce type de donnée peut aussi stocker la "non-valeur" NULL.
6.2 Date/Heure
Le type de données DATE, qui ne prend aucun paramétre, est utilisé pour stocker des valeurs de dates. Ces valeurs de date incluent l'heure lorsqu'elles sont stockées dans une colonne de la base de données. Les dates peuvent s'étendre du 1er janvier 4712 av. J.-C. au 31 décembre 4712. Les valeurs par défaut pour le type de données DATE sont les suivantes :
Date : premier jour du mois courant Heure : minuit
6.3 caractère
Le type de données caractère inclut:
le type dedonnées CHAR est destiné aux données de type caractère de longueur fixe et VARCHAR2 stocke des données de type caractère de longueur variable. Le type LONG stocke des chaînes de longueur variable, RAW et LONG RAW stockent des données binaires ou des chaînes d'octets. Les types de données CHAR, VARCHAR2 et RAW prennent un paramétre optionnel pour spécifier la longueur ( type(max_len) ).
Ce paramétre de longueur, max_len, doit être un entier littéral et non une constante ou une variable. La table ci-dessous montre les longueurs maximales et les largeurs des colonnes de la base pour les types de données caractère.
Avec cette table, on peut voir la contrainte d'introduction de données CHAR, VARCHAR2 et RAW dans des colonnes de même type de la base de données. La limite est la largeur de la colonne. Toutefois, on peut insérer des données de type LONG et LONG RAW de n'importe quelle longueur dans des colonnes similaires parce que la largeur de colonne est bien plus grande.
CHAR, VARCHAR2, LONG, RAW et LONG RAW.
Ce paramétre de longueur, max_len, doit être un entier littéral et non une constante ou une variable. La table ci-dessous montre les longueurs maximales et les largeurs des colonnes de la base pour les types de données caractère.
Type de données | CHAR | VARCHAR2 | LONG | RAW | LONG RAW |
Longueur maximale | 32767 | 32767 | 32760 | 32767 | 32760 |
Largeur maximale de colonne | 255 | 2000 | 2147483647 | 255 | 2147483647 |
6.4 Nombre
Il y a deux types de données dans la catégorie des nombres :
BINARY_INTEGER et NUMBER.
BINARY_INTEGER :
stocke des entiers signés sur l'étendue de ..231 à 231 .. 1. L'utilisation la plus courante de ce type de donnée est celle d'un index pour des tables PL/SQL. Le stockage de nombres de taille fixe ou flottants de n'importe quelle taille est possible avec le type de donnée NUMBER. Pour des nombres flottants, la précision et l'échelle peuvent être spécifiés avec le format suivant :
NUMBER(10,2)
Une variable déclarée de cette manière a un maximum de 10 chiffres et l'arrondi se fait à deux décimales. La précision par défaut est le plus grand entier supporté par le systéme et 0 est l'échelle par défaut. L'intervalle de précision va de 1 à 38 alors que l'échelle va de ..84 à 127.
BINARY_INTEGER et NUMBER.
BINARY_INTEGER :
stocke des entiers signés sur l'étendue de ..231 à 231 .. 1. L'utilisation la plus courante de ce type de donnée est celle d'un index pour des tables PL/SQL. Le stockage de nombres de taille fixe ou flottants de n'importe quelle taille est possible avec le type de donnée NUMBER. Pour des nombres flottants, la précision et l'échelle peuvent être spécifiés avec le format suivant :
NUMBER(10,2)
Une variable déclarée de cette manière a un maximum de 10 chiffres et l'arrondi se fait à deux décimales. La précision par défaut est le plus grand entier supporté par le systéme et 0 est l'échelle par défaut. L'intervalle de précision va de 1 à 38 alors que l'échelle va de ..84 à 127.
7 Types de données composées
Les deux types de données composites de PL/SQL sont TABLE et RECORD. Le type de donnée TABLE permet à l'utilisateur de définir un tableau PL/SQL. Le type de données RECORD permet d'aller au-delà de l'attribut de variable %ROWTYPE ; avec ce type, on peut spécifier des champs définis par l'utilisateur et des types de données pour ces champs.
7.1 Traitement des tableaux
Le type de données composé TABLE donne au développeur un mécanisme pour traiter les tableaux. Bien que ce type soit limité à une colonne d'information par tableau PL/SQL, on peut stocker n'importe quel nombre de lignes pour cette colonne. Les versions ultérieures d'Oracle définiront plus de exibilité dans l'emploi des tableaux.
Ce qui suit illustre comment on peut définir un tableau PL/SQL nommée g_recip_list (l'information sera utilisée globalement) dans l'exemple de order_total.
Pour initialiser un tableau, il faut tout d'abord définir un nom de tableau ou un type. Dans l'exemple précédent, c'est RecipientTabTyp. Cette colonne de tableau est définie comme un nombre avec au maximum 22 chiffres. La colonne peut être définie avec n'importe quel type de données valide de PL/SQL ; toutefois, la clé primaire, ou INDEX, doit être de type BINARY_INTEGER. Aprés avoir défini la structure du tableau, elle peut être utilisée dans des définitions de variables, comme c'est le cas pour RecipientTabTyp dans l'exemple précédent.
Ce qui suit illustre comment on peut définir un tableau PL/SQL nommée g_recip_list (l'information sera utilisée globalement) dans l'exemple de order_total.
TYPE RecipientTabTyp IS TABLE OF NUMBER(22) INDEX BY BINARY_INTEGER; ... g_recip_list RecipientTabTyp;
7.2 Constructions de tableaux
Aprés leur initialisation, les tableaux sont disponibles pour le stockage d'information. Pour stocker de l'information dans le tableau g_recip_list défini dans l'exemple précédent, il suffit de référencer le tableau avec une valeur numérique. Cela est illustré dans l'exemple suivant :
g_recip_list(j) := g_recipient_num(i)
Dans cet exemple, i et j sont des compteurs avec les valeurs 1::n. à partir du moment o.u de l'information est stockée dans un tableau, on peut y accéder, de manière numérique, comme indiqué dans l'exemple. Dans ce cas, les lignes de g_recipient_num sont référencées pour stockage dans g_recip_list. Le fait de référencer une ligne non initialisée dans un tableau PL/SQL cause une erreur "NO_DATA_FOUND" (voir la section sur le traitement des exceptions plus loin).7.3 Traitement des enregistrements
Le type de donnée composite RECORD procure au développeur un mécanisme pour traiter les enregistrements comme décrit précédemment. Bien que l'on ne puisse pas initialiser un tableau au moment de sa déclaration, il est possible de le faire avec des enregistrements, comme illustré dans l'exemple suivant :
La défini tion d'un enregistrement de type LineRecTyp permet des déclarations telles que li_info de ce type. Cette méthode de déclaration d'enregistrement peut être utilisée à la place de la déclaration li_info dans l'exemple %ROWTYPE précédent. Tout comme avec %ROWTYPE, les références aux données des enregistrements se font avec la notation " . " .
Dans ce cas, tous les champs de li_info réçoivent des valeurs provenant des informations extraites par la commande FETCH sur le curseur c_line_item.
TYPE LineRecTyp IS RECORD (merch_gross NUMBER := 0, recip_num NUMBER := 0 ); ... li_info LineRecTyp;
g_order_merch_total := g_order_merch_total + li_info.merch_gross;
Il y a trois moyens de donner une valeur à un enregistrement. Tout d'abord, une valeur peut être donnée à un champ d'un enregistrement tout comme on donne une valeur à n'importe quelle variable.
li_info.merch_gross := 10.50;
Une seconde méthode est de donner une valeur à tous les champs à la fois en utilisant deux enregistrements qui sont déclarés de même type. Supposons que new_li_info est une seconde variable de type LineRecTyp :
new_li_info := li_info;
Cette instruction donne à tous les champs de new_li_info les valeurs des mêmes champs de li_info. Il n'est pas possible d'aécter des valeurs d'enregistrements de différents types entre eux. Une troisième manière de donner des valeurs aux champs d'un enregistrement consiste à utiliser les instructions SQL SELECT ou FETCH.
OPEN c_line_item; ... FETCH c_line_item INTO li_info;
Variables et Constantes
Dan un programme PL/SQL on accède aux données en le utilisant comme des variables ou des constantes. Avant d'utiliser une variable ou une constante on doit les déclarer.
On déclare comme constante une valeur qu'on gardera inchangée tout au long du programme ; au moment de la déclaration on procède comme suit :
On déclare comme constante une valeur qu'on gardera inchangée tout au long du programme ; au moment de la déclaration on procède comme suit :
DECLARE credit_limit CONSTANT REAL := 5000.00; max_jour_dans_un_an CONSTANT INTEGER := 366; legende_urbaine CONSTANT BOOLEAN := FALSE;
Ici on vient donc de déclarer les trois constantes credit_limit, max_jour_dans_un_an, legende_urbaine et à chaque fois qu'on aura dans le programme legende_urbaine on saura que l'on est en train de parlez de la valeur FALSE,Déclarations
Les valeurs qu'on utilise dans un programme proviennent des variables et des constantes qu'on définit au fur et à mesure; les variables, comme le nom l'indique change en fonction de ce qu'on décide à travers les instructions du programme tandis que les contantes non ; quand on parlera de credit_limit dans un programme utilisant la déclaration des constantes faites ci-dessus, on saura que c'est de la valeur "5000.00" qu'on est en train de parler. Avec PL/SQL la déclaration se fait dans la partie reservée à cette effet dans n'importe quel block PL/SQL, sous-programme ou package. La déclaration reserve ainsi un espace mémoire spécifique à chaque type de données . Cette partie est précédée par le mot prédéfini DECLARE comme suit :
Exemples :
Dans la première déclaration on a une variable appelée anniversaire et qui est de type DATE et dans la deuxième déclarartion une variable appelée emp_compt de type SMALLINT initialisée à zéro ("0" - on verra par la suite l'opérarteur d'assignation ":=").
Par defaut, si une variable n'est pas initialisée au moment de sa déclaration d'une façon explicite, le système lui assigne la valeur NULL.
Les valeurs qu'on utilise dans un programme proviennent des variables et des constantes qu'on définit au fur et à mesure; les variables, comme le nom l'indique change en fonction de ce qu'on décide à travers les instructions du programme tandis que les contantes non ; quand on parlera de credit_limit dans un programme utilisant la déclaration des constantes faites ci-dessus, on saura que c'est de la valeur "5000.00" qu'on est en train de parler. Avec PL/SQL la déclaration se fait dans la partie reservée à cette effet dans n'importe quel block PL/SQL, sous-programme ou package. La déclaration reserve ainsi un espace mémoire spécifique à chaque type de données . Cette partie est précédée par le mot prédéfini DECLARE comme suit :
Exemples :
DECLARE anniversaire DATE; emp_compt SMALLINT := 0;
Par defaut, si une variable n'est pas initialisée au moment de sa déclaration d'une façon explicite, le système lui assigne la valeur NULL.
Constantes
Comme on vient de le voir dans l'exemple ci-dessus la déclaration d'une constante a une syntaxe bien précise :
Comme on vient de le voir dans l'exemple ci-dessus la déclaration d'une constante a une syntaxe bien précise :
DECLARE ... --- les déclaration qui précident s'il y en a nom_constante CONSTANTTYPE_CONSTANTE := valeur_constante; ... --- les déclarations qui suivent s'il y en a et la suite du programme
La déclaration d'une variable peut se faire en utilisant le mot prédéfini DEFAULT au lieu de l'opérateur d'assignation ":=" ; on a donc les deux façons de faire suivantes qui sont équivalentes:
En ce qui concerne les variables, on peut utiliser DEFAULT pour lui donner une valeur défaut et le sigen d'assignation est le signe normal pour affecter une valeur à une variable.
Exemple:
On peut utiliser DEFAULT pour initialiser les paramètres d'un sous-programme, d'un curseur et les champs d'un record défini par l'utilisateur.
blood_type CHAR := 'O';-- est identique à la déclaration ci-dessus blood_type CHAR DEFAULT 'O';
Exemple:
heures_travaillees INTEGER DEFAULT 40; nombre_employees INTEGER := 0;
DECLARE acct_id INTEGER(4) NOT NULL := 9999;
Exemple d'utilisation de %TYPE
Note that variables declared using %TYPE are treated like those declared using a datatype specifier. For example, given the previous declarations, PL/SQL treats debit like a PLS_INTEGER variable. A %TYPE declaration can also include an initialization clause. The %TYPE attribute is particularly useful when declaring variables that refer to database columns. You can reference a table and column, or you can reference an owner, table, and column, as in:
When you use table_name.column_name.%TYPE to declare a variable, you do not need to know the actual datatype, and attributes such as precision, scale, and length. If the database definition of the column changes, the datatype of the variable changes accordingly at run time. However, %TYPE variables do not inherit column constraints, such as the NOT NULL or check constraint, or default values. For example, even though the database column empid is defined as NOT NULL in Example 2-7, you can assign a NULL to the variable v_empid. Example 2-7 Using %TYPE With Table Columns
See "Constraints and Default Values With Subtypes" for information on column constraints that are inherited by subtypes declared using %TYPE. Using the %ROWTYPE Attribute The %ROWTYPE attribute provides a record type that represents a row in a table or view. Columns in a row and corresponding fields in a record have the same names and datatypes. However, fields in a %ROWTYPE record do not inherit constraints, such as the NOT NULL or check constraint, or default values, as shown in Example 2-8. See also Example 3-11. Example 2-8 Using %ROWTYPE With Table Rows
The record can store an entire row of data selected from the table, or fetched from a cursor or strongly typed cursor variable as shown in Example 2-9. Example 2-9 Using the %ROWTYPE Attribute
Aggregate Assignment Although a %ROWTYPE declaration cannot include an initialization clause, there are ways to assign values to all fields in a record at once. You can assign one record to another if their declarations refer to the same table or cursor. Example 2-10 shows record assignments that are allowed. Example 2-10 Assigning Values to a Record With a %ROWTYPE Declaration
You can assign a list of column values to a record by using the SELECT or FETCH statement, as the following example shows. The column names must appear in the order in which they were defined by the CREATE TABLE or CREATE VIEW statement.
Utilisation des Alias Select-list items fetched from a cursor associated with %ROWTYPE must have simple names or, if they are expressions, must have aliases. Example 2-11 uses an alias called complete_name to represent the concatenation of two columns: Example 2-11 Using an Alias for Column Names
Des langages permettent de déclarer des variables de même type en les séparant les uns des autres par des virgules ; PL/SQL quant à lui ne permet pas de le faire. Il oblige la déclaration de chaque variable indépendemment des autres.
DECLARE credit PLS_INTEGER RANGE 1000..25000; debit credit%TYPE; v_name VARCHAR2(20); name VARCHAR2(20) NOT NULL := 'Omara Aly'; -- If we increase the length of NAME, the other variables become longer also upper_name name%TYPE := UPPER(name); lower_name name%TYPE := LOWER(name); init_name name%TYPE := INITCAP(name); BEGIN -- display inherited default values DBMS_OUTPUT.PUT_LINE('name: ' || name || ' upper_name: ' || upper_name || ' lower_name: ' || lower_name || ' init_name: ' || init_name); -- lower_name := 'Omara Aly Mohamed'; invalid, character string is too long -- lower_name := NULL; invalid, NOT NULL CONSTRAINT -- debit := 50000; invalid, value out of range END; /
DECLARE -- If the length of the column ever changes, this code -- will use the new length automatically. the_trigger user_triggers.trigger_name%TYPE;
CREATE TABLE employees_temp (empid NUMBER(6) NOT NULL PRIMARY KEY, deptid NUMBER(6) CONSTRAINT check_deptid CHECK (deptid BETWEEN 100 AND 200), deptname VARCHAR2(30) DEFAULT 'Sales'); DECLARE v_empid employees_temp.empid%TYPE; v_deptid employees_temp.deptid%TYPE; v_deptname employees_temp.deptname%TYPE; BEGIN v_empid := NULL; -- this works, null constraint is not inherited -- v_empid := 10000002; -- invalid, number precision too large v_deptid := 50; -- this works, check constraint is not inherited -- the default value is not inherited in the following DBMS_OUTPUT.PUT_LINE('v_deptname: ' || v_deptname); END; /
DECLARE emprec employees_temp%ROWTYPE; BEGIN emprec.empid := NULL; -- this works, null constraint is not inherited -- emprec.empid := 10000002; -- invalid, number precision too large emprec.deptid := 50; -- this works, check constraint is not inherited -- the default value is not inherited in the following DBMS_OUTPUT.PUT_LINE('emprec.deptname: ' || emprec.deptname); END; /
DECLARE -- %ROWTYPE can include all the columns in a table... emp_rec employees%ROWTYPE; -- ...or a subset of the columns, based on a cursor. CURSOR c1 IS SELECT department_id, department_name FROM departments; dept_rec c1%ROWTYPE; -- Could even make a %ROWTYPE with columns from multiple tables. CURSOR c2 IS SELECT employee_id, email, employees.manager_id, location_id FROM employees, departments WHERE employees.department_id = departments.department_id; join_rec c2%ROWTYPE; BEGIN -- We know EMP_REC can hold a row from the EMPLOYEES table. SELECT * INTO emp_rec FROM employees WHERE ROWNUM < 2; -- We can refer to the fields of EMP_REC using column names -- from the EMPLOYEES table. IF emp_rec.department_id = 20 AND emp_rec.last_name = 'JOHNSON' THEN emp_rec.salary := emp_rec.salary * 1.15; END IF; END; /
DECLARE dept_rec1 departments%ROWTYPE; dept_rec2 departments%ROWTYPE; CURSOR c1 IS SELECT department_id, location_id FROM departments; dept_rec3 c1%ROWTYPE; BEGIN dept_rec1 := dept_rec2; -- allowed -- dept_rec2 refers to a table, dept_rec3 refers to a cursor -- dept_rec2 := dept_rec3; -- not allowed END; /
DECLARE dept_rec departments%ROWTYPE; BEGIN SELECT * INTO dept_rec FROM departments WHERE department_id = 30 and ROWNUM < 2; END; /
BEGIN -- We assign an alias (complete_name) to the expression value, because -- it has no column name. FOR item IN ( SELECT first_name || ' ' || last_name complete_name FROM employees WHERE ROWNUM < 11 ) LOOP -- Now we can refer to the field in the record using this alias. DBMS_OUTPUT.PUT_LINE('Employee name: ' || item.complete_name); END LOOP; END; /
DECLARE -- la ligne ci-dessus n'est pas valable -- i, j, k, l SMALLINT; -- Instead, declare each separately. -- ce qui suit est valable i SMALLINT; j SMALLINT; -- To save space, you can declare more than one on a line. k SMALLINT; l SMALLINT;
8 Structures de contrôle
Tout langage procédural a des structures de contrôle qui permettent de traiter l'information d'une manière logique en contrôlant le ot des informations. Les structures disponibles au sein de PL/SQL incluent IF-THENELSE, LOOP et EXIT-WHEN. Ces structures procurent de la exibilité dans la manipulation des données de la base de données.
8.1 Boucles
L'utilisation de la commande LOOP fournit un traitement itératif basé sur des choix logiques. La construction de base des boucles "LOOP" est montrée dans l'exemple suivant :
Pour sortir d'une boucle de ce genre, il faut une commande EXIT ou GOTO basée sur une condition du traitement. En cas de levée d'exception dé.nie par l'utilisateur, la boucle LOOP s'achéve aussi. Examinons maintenant trois types de boucles PL/SQL qui dé.nissent des conditions explicites de terminaison. Une boucle peut être nommée comme cela a été montré dans l'exemple en utilisant une étiquette telle que <<nom>> juste avant l'instruction LOOP. Bien que ce ne soit pas obligatoire, l'étiquetage permet de garder une meilleure trace de l'imbrication des boucles.
<<nom>> LOOP (traitement répétitif) END LOOP nom;
8.2 Boucles WHILE
La boucle WHILE vérié l'état d'une expression PL/SQL qui doit s'évaluer à TRUE, FALSE ou NULL au début de chaque cycle de traitement. Ce qui suit est un exemple d'utilisation de boucles WHILE :
Comme indiqué, le programme évalue l'expression au début de chaque cycle de boucle. Le programme exécute le traitement de la boucle si l'expression s'évalue à TRUE. Une valeur FALSE ou NULL termine la boucle. Les itérations à travers la boucle sont exclusivement déterminées par l'évaluation de l'expression.
WHILE (expression) LOOP (traitement de boucle) END LOOP;
8.3 Boucles FOR numériques
Les itérations de boucles peuvent être contrôlées avec des boucles FOR numériques. Ce mécanisme permet au développeur d'établir un intervalle d'entiers pour lesquels la boucle va être itérée. L'exemple suivant du package order_total illustre les boucles numériques FOR :
<<recip_list>>
Dans cet exemple, la boucle est itérée pour les entiers de 1 jusqu'à la valeur de g_line_counter. La valeur de l'index de boucle i est véri.ée au début de la boucle et incrémentée à la .n de la boucle. Lorsque i est égal à g line counter + 1, la boucle termine.
<<recip_list>>
FOR i in 1..g_line_counter LOOP (traitement de boucle) END LOOP recip_list;
8.4 Boucles FOR de curseurs
Les boucles FOR de curseurs combinent le contrôle de curseurs et des structures de contrôle supplémentaires pour la manipulation d'informations de bases de données. L'index de boucle, l'ouverture de curseur, le FETCH et la fermeture de curseur sont tous implicites lorsque l'on utilise des boucles FOR de curseurs. Considérons l'exemple suivant :
Comme montré, le programme déclare explicitement le curseur c_line_item avant qu'il ne soit référenc é dans la boucle FOR. Lorsque le programme pénétre dans la boucle FOR, le code ouvre implicitement c_line_item et crée implicitement l'enregistrement li_info comme si la déclaration suivante était présente : li_info c_line_item%ROWTYPE; Dés l'entrée dans la boucle, le programme peut référencer les champs de l'enregistrement li_info qui re.coivent des valeurs par le FETCH implicite à l'intérieur de la boucle FOR. Les champs de li_info re étent la ligne extraite par le curseur c_line_item. Lorsqu'il n'y a plus de données pour FETCH, le curseur c_line_item est implicitement fermé. Il n'est pas possible de référencer l'information contenue dans li_info en dehors de la boucle de curseur.
CURSOR c_line_item IS (instruction sql) BEGIN FOR li_info IN c_line_item LOOP (traitement de l'enregistrement extrait) END LOOP; END;
8.5 Structure de contrôle conditionnelle
La structure IF-THEN-ELSE permet d'avoir des traitements qui dépendent de certaines conditions. Par exemple, considérons des commandes de marchandises avec des éléments sur plusieurs lignes o.u une liste de destinataires est construite. En utilisant des structures de contrôle conditionnelles et itératives pour construire la liste des destinataires, le code est le suivant :
PROCEDURE
Dans l'exemple order_total, le sous-programme init_recip_list construit une liste de numéros de destinataires uniques pour le calcul des frais de port supplémentaires. Il y a une boucle de contrôle FOR qui parcourt chaque numéro de destinataire trouvé sur une commande particuliére. Le tableau g_recip_list est initialisé avec le premier numéro de destinataire et les numéros suivants sont comparés avec tous les numéros uniques dans g_recip_list jusqu'à ce qu'une liste de tous les destinataires ait été rassemblée. Cet exemple illustre aussi l'extension ELSIF de IF-THEN-ELSE. Cette partie fournit une structure de contrôle supplémentaire avec des test de contraintes additionnelles. L'emploi de ELSIF requiert aussi un THEN.
Un autre exemple est l'emploi de EXIT-WHEN qui permet la complétion d'une boucle lorsque certaines conditions sont satisfaites. Considérons l'exemple de sortie de boucle FETCH suivant :
Dans cet exemple, la boucle est terminée lorsqu'on ne trouve plus de données pour satisfaire le SELECT du curseur c_line_item. L'emploi de %NOTFOUND ou %FOUND peut causer des boucles in.nies si l'on ne vérié pas que ces attributs sont évalués à NULL dans un test logique EXIT-WHEN.
PROCEDURE
init_recip_list IS recipient_num NUMBER; i BINARY_INTEGER; j BINARY_INTEGER := 1; k BINARY_INTEGER; BEGIN g_out_msg := 'init_recip_list'; <<recip_list>> FOR i in 1..g_line_counter LOOP IF i = 1 THEN g_recip_list(j) := g_recipient_num(i); j := j + 1; g_recip_list(j) := 0; ELSE FOR k in 1..j LOOP IF g_recipient_num(i) = g_recip_list(k) THEN exit; ELSIF k = j THEN g_recip_list(j) := g_recipient_num(i); j := j + 1; 14 g_recip_list(j) := 0; end IF; end LOOP; end IF; end LOOP recip_list; END;
Un autre exemple est l'emploi de EXIT-WHEN qui permet la complétion d'une boucle lorsque certaines conditions sont satisfaites. Considérons l'exemple de sortie de boucle FETCH suivant :
open c_line_item; loop fetch c_line_item into li_info; EXIT WHEN (c_line_item%NOTFOUND) or (c_line_item%NOTFOUND is NULL);
4 Curseurs
PL/SQL utilise des curseurs pour tous les accés à des informations de la base de données. Le langage supporte à la fois l'emploi de curseurs implicites et explicites. Les curseurs implicites sont ceux qui sont établis lorsqu'un curseur explicite n'a pas été déclaré. Il faut utiliser des curseurs explicites ou des curseurs de boucles FOR dans toutes les requêtes qui renvoient plusieurs lignes.
4.1 Déclaration de curseurs
Les curseurs sont définis dans la zone des variables de sous-programmes PL/SQL en utilisant l'instruction CURSOR name IS, comme montré dans l'exemple suivant :
L'instruction SQL peut être n'importe quelle requête valide. Aprés l'initialisation d'un curseur, les actions d'un curseur peuvent être contrôlées avec les instructions OPEN, FETCH et CLOSE.
CURSOR c_line_item IS (instruction sql)
4.2 Le contrôle d'un curseur
Pour utiliser un curseur a.n de manipuler des données, il faut utiliser l'instruction OPEN name pour exécuter la requête et identiér toutes les lignes qui satisfont le critére de sélection. Les extractions ultérieures de lignes sont réalisées avec l'instruction FETCH. Lorsque toutes les données sont traitées, l'instruction CLOSE clôt toute activité associée avec le curseur ouvert. Ce qui suit est un exemple de contrôle de curseur :
Ce code ouvre le curseur c_line_item et traite les lignes extraites. Aprés l'extraction et le traitement de toute l'information, le curseur est fermé. Le traitement des lignes extraites est typiquement contrôlé par des itérations de boucles comme discuté plus loin.
OPEN c_line_item; ... FETCH c_line_item INTO li_info; ... (traitement de la ligne extraite) ... CLOSE c_line_item;
4.3 Attributs des curseurs explicites
Il y a quatre attributs associés aux curseurs PL/SQL.
Tous les attributs de curseur s'évaluent à TRUE, FALSE ou NULL, en fonction de la situation. L'attribut %NOTFOUND s'évalue à FALSE quand une ligne est extraite, TRUE si le dernier FETCH n'a pas renvoyé une valeur et NULL si le curseur SELECT n'a pas renvoyé de données. L'attribut %FOUND est l'opposé logique de %NOTFOUND par rapport à TRUE et FALSE, mais s'évalue néanmoins à NULL si le curseur ne renvoie pas de données. %ROWCOUNT peut être utilisé pour déterminer combien de rangées ont été sélectionnées à un moment donné dans le FETCH. Cet attribut est incrémenté après la sélection réussie d'une ligne. De plus, %ROWCOUNT est à zéro quand le curseur est ouvert pour la premiére fois. Le dernier attribut, %ISOPEN, est ou bien TRUE ou bien FALSE, suivant que le curseur associé est ouvert ou non. Avant que le curseur ne soit ouvert et aprés qu'il soit fermé, %ISOPEN vaut FALSE. Dans les autres cas, cet attribut s'évalue à TRUE.
{ %NOTFOUND { %FOUND { %ROWCOUNT { %ISOPEN
4.4 Paramétres des curseurs
On peut spéciér des paramétres pour les curseurs de la même manière que pour des sous-programmes. L'exemple suivant illustre la syntaxe de déclaration de curseurs avec des paramétres :
Le mode des paramétres est toujours IN, mais les types de données peuvent être n'importe quels types de données valides. Un paramétre de curseur ne peut être référencé que pendant la requête déclarée. La exibilité au sein des paramétres de curseurs permet au développeur de passer di.érents nombres de paramétres à un curseur en utilisant le mécanisme des paramétres par défaut. Ceci est illustré dans l'exemple ci-dessous :
En utilisant la déclaration INTEGER DEFAULT, on peut passer tous, un, ou aucun des paramétres de ce curseur en fonction du code appelant.
CURSOR c_line_item (order_num IN NUMBER) IS SELECT merch_gross, recipient_num FROM line_item WHERE order_num = g_order_num;
CURSOR c_line_item (order_num INTEGER DEFAULT 100, line_num INTEGER DEFAULT 1) IS ...
4.5 Création de packages de curseurs
Un package de curseurs est similaire à un package de procédures en ce que l'on spécié le curseur et son attribut de retour, %TYPE or %ROWTYPE, dans la zone de spéci.cation du package. On spécié ensuite le corps du curseur dans la zone de spéci.cation du corps du package. Le fait de regrouper un curseur de cette manière donne la exibilité de changer le corps du curseur sans avoir à recompiler les applications qui font référence à la procédure groupée. Ce qui suit est un exemple de package de curseur :
Dans cet exemple, la variable rendue est de même type que line_item.item_merch_gross. On peut utiliser l'attribut %ROWTYPE pour spécifier un enregistrement RETURN qui re éte une ligne dans une table de la base de données.
CREATE OR REPLACE PACKAGE order_total AS CURSOR c_line_item RETURN line_item.merch_gross%TYPE; ... END order_total; CREATE OR REPLACE PACKAGE BODY order_total AS CURSOR c_line_item RETURN line_item.merch_gross%TYPE SELECT merch_gross FROM line_item WHERE order_num = g_order_num; ... END order_total;
5 Visibilité des Variables
Variables de procédures
aspect important d'un langage est la manière de dé.nir les variables. Une fois que les variables sont définies, PL/SQL permet de les utiliser dans des commandes SQL ou dans d'autres commandes du langage. La définition de constantes au sein de PL/SQL suit les mêmes régles. De même, on peut dé.nir des variables et constantes locales à un sous-programme ou globales à un package qui est créé. Il faut dé.nir les variables et les constantes avant de les référencer dans une autre construction.
aspect important d'un langage est la manière de dé.nir les variables. Une fois que les variables sont définies, PL/SQL permet de les utiliser dans des commandes SQL ou dans d'autres commandes du langage. La définition de constantes au sein de PL/SQL suit les mêmes régles. De même, on peut dé.nir des variables et constantes locales à un sous-programme ou globales à un package qui est créé. Il faut dé.nir les variables et les constantes avant de les référencer dans une autre construction.
5.1 Déclaration de variables
Tout type de donnée de PL/SQL ou SQL est un type valide dans une définition de variable. Les types de données les plus utilisés sont VARCHAR2, DATE, NUMBER (types de SQL), BOOLEAN et BINARY_INTEGER (types de PL/SQL). Les types de données scalaires et composés de PL/SQL sont discutés de manière plus détaillée plus loin.
5.2 Variables locales
Supposons que l'on veuille déclarer deux variables locales nommées merch_gross et recip_count. La première, merch_gross, va contenir un nombre flottant à 10 chiffres arrondi à deux décimales ; recip_count va contenir un compteur entier. Ces variables sont déclarées de la manière suivante :
On peut aussi déclarer merch_gross dans cet exemple avec NUMBER(10,2) pour expliciter le nombre total de chiffres et l'arrondi. Toutefois, si une telle déclaration est liée à un champ de la base de données, elle doit changer lorsque la définition de la base de données change. On peut utiliser deux méthodes pour donner des valeurs aux variables. La première est d'utiliser un opérateur d'aéctation comme suit :
merch_gross NUMBER; recip_count BINARY_INTEGER;
merch_gross := 10.50;
La seconde méthode est d'utiliser une commande SQL SELECT ou FETCH qui dé.nit une valeur de la base de données comme suit :
SELECT merch_gross INTO merch_gross FROM line_item WHERE order_num = g_order_num;
5.3 Constantes locales
La déclaration d'une constante est similaire à la déclaration d'une variable sauf que le mot clé CONSTANT doit suivre le nom de la ® variable¯. Il faut immédiatement donner une valeur à la constante. tax_rate CONSTANT NUMBER := 0.03;
5.4 Variables globales
Les variables globales sont définies de la même manière que des variables locales, mais elles sont définies en dehors de toute définition de procédure. Supposons que l'on veuille définir les variables g_order_num et g_recip_counter pour qu'elles soient accessibles depuis tous les sous-programmes du package. Cela peut se faire ainsi :
Il faut noter que ces variables globales sont définies dans la zone de spécification du corps du package pour éviter qu'elles ne soient ®vues¯ par des applications qui appellent la procédure groupée order_total. Si l'on utilise des noms de variables identiques à des noms de colonnes de la base de données, les résultats d'opérations SELECT ou UPDATE impliquant ces variables sont imprévisibles.
CREATE OR REPLACE PACKAGE BODY order_total AS ... g_order_num NUMBER; g_recip_counter BINARY_INTEGER; ... PROCEDURE ...
5.5 Mot-clé DEFAULT
Le mot-clé DEFAULT permet d'initialiser des variables sans utiliser l'opérateur d'aéctation comme dans l'exemple suivant :
merch_gross NUMBER DEFAULT 10.50;
On peut aussi utiliser le mot-clé DEFAULT pour initialiser les paramétres d'un curseur dans un sousprogramme ou des champs dans un enregistrement défini par l'utilisateur.5.6 Attributs des variables et constantes
Les deux attributs des variables et constantes PL/SQL sont %TYPE et %ROWTYPE. L'attribut %TYPE permet de déclarer des variables similaires à des colonnes de la base de données sans conna^.tre le type de la colonne. merch_gross peut être dé.ni de la manière suivante :
Nom de la colonne order_num line_num merch_gross recipient_num Donnée 100 1 10.50 1000 Un curseur peut être défini au sein d'une procédure (voir plus haut) a.n d'extraire des informations de la table LINE_ITEM. En même temps que le curseur, on dé.nit une variable ROWTYPE pour stocker les champs de cette ligne comme suit :
Après le FETCH, on utilise la notation " . " pour accèder à l'information extraite de la base de données.
merch_gross line_item.merch_gross%TYPE;
La définition d'une variable de cette manière permet de rendre eéctifs des changements à la base de données lors de la prochaine compilation sans changer le code. L'attribut %ROWTYPE permet de représenter une ligne d'une table avec un type de donnée enregistrement qui masque les colonnes de la base de données. Considérons l'échantillon de données dans la table LINE_ITEM ci-dessous :Nom de la colonne order_num line_num merch_gross recipient_num Donnée 100 1 10.50 1000 Un curseur peut être défini au sein d'une procédure (voir plus haut) a.n d'extraire des informations de la table LINE_ITEM. En même temps que le curseur, on dé.nit une variable ROWTYPE pour stocker les champs de cette ligne comme suit :
CURSOR c_line_item IS SELECT merch_gross, recipient_num FROM line_item WHERE order_num = g_ordnum; li_info c_line_item%ROWTYPE; Pour extraire les données, on utilise FETCH : FETCH c_line_item INTO li_info;
g_order_merch_total := g_order_merch_total + li_info.merch_gross;
Packages
Un ensemble complet de programmes PL/SQL effectuant une certaine tâche est appellé un package. Avant d'étudier les divers aspects du langage PL/SQL, examinons la syntaxe de création d'un package pour l'écriture d'un script facilitant la maintenance en cas de changements. Ce code est la première étape d'un package calculant les totaux en dollars dans une commande de marchandises. Cet exemple illustre quelques commandes de création de packages et de scripts.
set echo on spool order_total CREATE OR REPLACE PACKAGE order_total AS (spécifications du package) END order_total CREATE OR REPLACE PACKAGE BODY order_total AS (spécifications du corps du package) END order_total; DROP PUBLIC SYNONYM order_total; CREATE PUBLIC SYNONYM order_total for order_total; GRANT EXECUTE ON order_total TO PUBLIC; spool off SELECT * FROM user_errors WHERE name='ORDER_TOTAL' ;