You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1545 lines
36 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

\documentclass[a4paper,11pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage[a4paper,hmargin=20mm,vmargin=30mm]{geometry}%\usepackage{fullpage}
\usepackage{lastpage}
\usepackage{tikz,pgflibraryarrows,pgffor,pgflibrarysnakes}
\usetikzlibrary{decorations.pathreplacing}
\usepackage{url}
\usepackage{comment}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{eurosym}
\theoremstyle{definition}
\newtheorem{exemple}{Exemple}[section]
\newtheorem{exercice}{Exercice}
\newtheorem{remarque}{Remarque}[section]
\newtheorem{definition}{Définition}[section]
\usepackage{makeidx}
\usepackage[columnsep=9pt]{idxlayout}
\usepackage{fancyvrb}
%\makeindex
\selectlanguage{french}
\usepackage[font=small,labelfont=bf,justification=centering]{caption}
\captionsetup[table]{name=Tableau}
\usepackage{latexsym}
\usepackage{amsfonts}
\usepackage[normalem]{ulem}
\usepackage{array}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{subfig}
\usepackage{wrapfig}
\usepackage{wasysym}
\usepackage{enumitem}
\usepackage{adjustbox}
\usepackage{longtable}
\usepackage{changepage}
\usepackage{setspace}
\usepackage{hhline}
\usepackage{multicol}
\usepackage{float}
\usepackage{multirow}
\usepackage{slashbox}
\usepackage{color, colortbl}
\definecolor{Gray}{gray}{0.9}
\usepackage{fancyvrb}
\usepackage{fancyhdr}% fancy header
\usepackage{varwidth}
\usepackage{alltt}
\fancypagestyle{monstyle}{
%\fancyhead{}
\renewcommand{\headrulewidth}{1pt}
%% %\renewcommand{\footrulewidth}{0.4pt}
\newcommand{\plsql}{PL/SQL}
% \fancyhead[LE]{\slshape \thepage/ \pageref{LastPage}}
%% \fancyhead[RO]{\slshape \thepage/ \pageref{LastPage}}
%\fancyhf{}
%\fancyhead[LE]{\slshape LE}
%\fancyhead[CE]{\slshape CE}
%\fancyhead[RE]{\slshape RE}
\fancyhead[LO]{\bfseries\rightmark}
%\fancyhead[CO]{\slshape APF}
\fancyhead[RO]{\bfseries\leftmark}
%% %\fancyfoot{}
% \fancyfoot[LE,RO]{}
\fancyfoot[CO,CE]{\slshape\thepage/\pageref{LastPage}}
%% %\fancyfoot[LO,RE]{\small\slshape \ddmmyyyydate version du \today}
}
% \pagestyle{fancy}
\pagestyle{monstyle}
\newcommand{\code}[1]{\texttt{#1}}
\usepackage{boxedminipage}
\newsavebox\svbx
\newif\ifcache
\long\def\cache#1{\ \newline
\setbox\svbx=\vbox{\leavevmode \newline \begin{spacing}{1.5}#1\end{spacing}}
\smallskip\par\noindent
\begin{boxedminipage}{\linewidth}
\ifcache
\leavevmode\hrule height 0pt\vskip \ht\svbx\hrule height 0pt
\else \unvbox\svbx
\fi
\end{boxedminipage}
\par\smallskip}
\cachefalse % version prof
%\cachetrue % version etudiant
\makeindex
\begin{document}
\begin{titlepage}
\begin{center}
\textsc{\Large IUT Informatique Aubière \hfill 2018 - 2019} \\[.5cm]
\hrule
\ \\[.5cm]
\vfill
\textsc{\LARGE Bases de données}
\vfill
\textsc{\LARGE \plsql{}}
\vfill
{\Large Pascale \textsc{Brigoulet}, Franck \textsc{Glaziou}, }
{\Large Pascal \textsc{Lafourcade} et Marie-Fran\c{c}oise
\textsc{Servajean}}
\vfill
\begin{tikzpicture}
%%% un triangle
%% horizonatale
\draw[blue,line width=1pt] (-1,0) -- (1,0);
\draw[blue,line width=1pt] (-2.5,-.875) -- (1.5,-.875);
\draw[blue,line width=1pt] (-1.5,-1.75) -- (3,-1.75);
\draw[blue,line width=1pt] (0,3.5) -- (1,3.5);
%% Croissante
\draw[blue,line width=1pt] (0,0) -- (1,1.75);
\draw[blue,line width=1pt] (-2.5,-.875) -- (0,3.5);
\draw[blue,line width=1pt] (-1,0) -- (1,3.5);
\draw[blue,line width=1pt] (3,-1.75) -- (3.5,-0.75);
%% Decroissante
\draw[blue,line width=1pt] (0.5,.875) -- (1.5,-.875);
\draw[blue,line width=1pt] (1,1.75) -- (3,-1.75);
\draw[blue,line width=1pt] (1,3.5) -- (3.5,-0.75);
\draw[blue,line width=1pt] (-2.5,-.875) -- (-1.5,-1.75);
\end{tikzpicture}
%% \begin{tikzpicture}
%% %%% un cube
%% \draw[red,line width=1pt] (0,.5) -- (1,0);
%% \draw[red,line width=1pt] (2,.5) -- (1,0);
%% \draw[red,line width=1pt] (0,1.5) -- (1,1);
%% %\draw[red,line width=1pt] (0,1.5) -- (1,2);
%% \draw[red,line width=1pt] (0,1.5) -- (.5,1.75);
%% %\draw[red,line width=1pt] (2,1.5) -- (1,2);
%% \draw[red,line width=1pt] (2,1.5) -- (1.5,1.75);
%% \draw[red,line width=1pt] (2,1.5) -- (1,1);
%% \draw[red,line width=1pt] (1,1) -- (1,0);
%% \draw[red,line width=1pt] (0,1.5) -- (0,.5);
%% \draw[red,line width=1pt] (2,1.5) -- (2,.5);
%% %% droite
%% %\draw[red,line width=1pt] (1.5,1.25) -- (2.5,0.75);
%% \draw[red,line width=1pt] (2,1) -- (2.5,0.75);
%% \draw[red,line width=1pt] (3.5,1.25) -- (2.5,0.75);
%% \draw[red,line width=1pt] (1.5,2.25) -- (2.5,1.75);
%% \draw[red,line width=1pt] (1.5,2.25) -- (2.5,2.75);
%% \draw[red,line width=1pt] (3.5,2.25) -- (2.5,2.75);
%% \draw[red,line width=1pt] (3.5,2.25) -- (2.5,1.75);
%% \draw[red,line width=1pt] (2.5,1.75) -- (2.5,.75);
%% %\draw[red,line width=1pt] (1.5,2.25) -- (1.5,1.25);
%% \draw[red,line width=1pt] (1.5,2.25) -- (1.5,1.75);
%% \draw[red,line width=1pt] (3.5,2.25) -- (3.5,1.25);
%% %% Haut
%% \draw[red,line width=1pt] (0,2) -- (1,1.5);
%% %\draw[red,line width=1pt] (2,2) -- (1,1.5);
%% \draw[red,line width=1pt] (1.5,1.75) -- (1,1.5);
%% \draw[red,line width=1pt] (0,3) -- (1,2.5);
%% \draw[red,line width=1pt] (0,3) -- (1,3.5);
%% \draw[red,line width=1pt] (2,3) -- (1,3.5);
%% \draw[red,line width=1pt] (2,3) -- (1,2.5);
%% \draw[red,line width=1pt] (1,2.5) -- (1,1.5);
%% \draw[red,line width=1pt] (0,3) -- (0,2);
%% %\draw[red,line width=1pt] (2,3) -- (2,2);
%% \draw[red,line width=1pt] (2,3) -- (2,2.5);
%% \end{tikzpicture}
\vfill
\includegraphics[width=5cm]{iut-uca.png}
\end{center}
\vfill
{\Large Nom : \\
Prénom : \\
Groupe : \\
}
%\url{http://mocodo.wingi.net/}
%\url{http://mirror.hmc.edu/ctan/graphics/pgf/contrib/tkz-orm/tkz-orm.pdf}
\end{titlepage}
%% \section*{Avant Propos}
%% Lobjectif de ce cours de base de données avancées est de
%% présenter
TD 1 : → Exo3
TD 2 : Exo 3, 5, 6 7
TD 3 : Reste
TD 4 : Partiel
%% \newpage
\tableofcontents
\newpage
\begin{center}
\LARGE {\bf \plsql{}}
\end{center}
PL/SQL (Procedural Language / Structured Query Language) est un
langage fondé sur les paradigmes de programmation procédurale et
structurée. C'est un langage propriétaire, créé par Oracle et utilisé
dans le cadre de bases de données relationnelles. Il permet de
combiner des requêtes SQL et des instructions procédurales (boucles,
conditions...), dans le but de créer des traitements complexes
destinés à être stockés sur le serveur de base de données (objets
serveur), comme des procédures stockées ou des déclencheurs.
\section{Fichier de commandes SQL/PLUS}
SQL/Plus est un utilitaire en ligne de commande d'Oracle qui permet
aux utilisateurs d'exécuter interactivement des commandes SQL et
PL/SQL. Il est ainsi possible de paramètrer les fichiers de commandes
SQL. Pour cela il est important de stocker des informations dans des
variable : \code{variable vnoproduit CHAR(6)}. Cette commande déclare
une «bind variable» utilisable dans une commande SQL. Les diff\'eretns
formats autorisés sont : \code{NUMBER}, \code{CHAR(n)} et
\code{VARCHAR2(n)}. Afin d'interagir avec l'utilisateur il faut
pouvoir il existe deux commandes :
\code{PROMPT taper le N° du produit}
Cette commande affiche le texte placé après prompt (pas de quote)
\code{ACCEPT vnoproduit}
Cette commande permet la saisie d'une variable au clavier (la déclare
si elle ne létait pas).
\code{SELECT * FROM tproduit WHERE N°produit = \&vnoproduit;}
\&{vnoproduit} permet d'acc\'eder \`a la chaîne de caractères
stock\'ee dans \code{vnoproduit}.
\code{PRINT vnoproduit}
Cette commande affiche la variable.
\section{Exécution du code PL/SQL}
En SQL, les commandes sont transmises les unes après les autres et
traitées séparément par le moteur SQL, comme le montre la
Figure~\ref{fig:plsql1}.
\begin{figure}[htb]
\begin{center}
\includegraphics[width=12cm]{plsql2.png}
\end{center}
\caption{}\label{fig:plsql1}
\end{figure}
En extension procédurale de SQL (\plsql{}), les blocs de commandes
sont transmis globalement au moteur SQL, comme le montre la
Figure~\ref{fig:plsql2}.
\begin{figure}[htb]
\begin{center}
\includegraphics[width=12cm]{plsql1.png}
\end{center}
\caption{Interaction entre PL/SQL et SQL.}\label{fig:plsql2}
\end{figure}
\section{Exemple de programme \plsql{}}
Le fichier de commandes ci-dessous permet la saisie de la référence
dun produit et laffichage de sa désignation ou dun  message
derreur si le N° de produit nexiste pas.
\begin{verbatim}
set echo off
set verify off
set feed off
variable vnoproduit char(4)
variable vdesignation varchar2(30)
prompt taper la référence du produit à rechercher :
accept vnoproduit
\end{verbatim}
\begin{SaveVerbatim}{VerbEnv}
declare
dnoproduit char(4);
begin
dnoproduit := &vnoproduit;
select designation into :vdesignation
from tproduit where noproduit = dnoproduit;
exception
when no_data_found then
:vdesignation := référence inconnue;
end;
\end{SaveVerbatim}
\fbox{\BUseVerbatim{VerbEnv}}
\begin{verbatim}
.
/
print vdesignation
set verify on
set feed on
set echo on
\end{verbatim}
\begin{remarque}
Ce fichier comporte trois parties :
\begin{itemize}
\item des commandes SQL+ pour la saisie de la référence produit,
\item un bloc \plsql{} pour accéder à la base,
\item des commandes SQL+ pour afficher le résultat.
\end{itemize}
\end{remarque}
\plsql{} ne comprend pas dinstruction de saisie ou daffichage. Les
commandes \code{SET} positionnent des variables denvironnement pour
éviter que le système naffiche des informations « polluant » le
résultat :
\begin{itemize}
\item \code{set echo off} évite que le système naffiche la commande
du bloc PL/SQL au moment où il lexécute,
\item \code{set verify off} évite que le système naffiche lopération
de substitution au moment où il la fait,
\item \code{set feed off} évite que le système naffiche le nombre de
lignes sélectionnées,
\item \code{.} le point indique la fin du mode PL/SQL,
\item \code{/} (seul sur une ligne) déclenche lexécution du bloc PL/SQL ou de l ordre SQL stocké dans le buffer.
\end{itemize}
\section{Structure d'un bloc \plsql{}}
\plsql{} n'interprète pas une commande, mais un ensemble de commandes
contenu dans un "bloc" \plsql{}. Un bloc est composé de trois
sections.
\begin{itemize}
\item Les sections \code{DECLARE} et \code{EXCEPTION} sont facultatives.
\item Chaque instruction, de n'importe quelle section, est terminée
par un \code{;}
\item Possibilité de placer des commentaires : \code{/* commentaire
sur plusieurs lignes commençant par au moins un espace */}
\end{itemize}
Attention aux points virgules: en cas doubli, Oracle affiche un N° de
ligne attendant la suite de commande SQL.
\begin{figure}[htb]
\begin{center}
\includegraphics[width=12cm]{bloc.png}
\end{center}
\caption{Structure d'un programme \plsql{}.}\label{fig:bloc}
\end{figure}
\section{Types de variables utilisés en \plsql{}}
Variables locales déclarées dans \code{DECLARE} et il est important de
mettre le type adapt\'e en suivant la syntaxe suivante :
\verb+nom_variable type := valeur;+
Les diff\'erents types possibles sont : \code{CHAR, NUMBER, DATE,
VARCHAR2}.
Il est n\'ecessaire de choisir le type \`a l'initialisation lors de la
déclaration et aussi sa valeur intiale comme le montre les deux
exemples suivants :
\begin{itemize}
\item \code{NB NUMBER := 1;}
\item \verb+nom_variable nomtable.nomcol %type+
\end{itemize}
\begin{exemple} Cr\'eation de variable.
\code{dnoprod tproduit.noproduit \%type;}
\code{ddesignation tproduit.designation \%type ;}
\end{exemple}
Il est possible de cr\'eer des tableaux en \plsql{}. %
%% Variables de l'environnement extérieur à \plsql{}:
%% \begin{itemize}
%% \item champs d'écran en SQL*Forms,
%% \item variables définies en langage hôte dans PRO*. Les variables de
%% lenvironnement sont toujours préfixées.
%\item
Les variables définies dans SQL*Plus par \code{VARIABLE} ou
\code{ACCEPT} sont préfixées de : pour affectation, de "\verb+&+" pour
substitution.
% \end{itemize}
\begin{exemple} Exemple simple d'utilisation de variable.
\begin{verbatim}
dnoproduit := '&vnoproduit';
select designation into :vdesignation
from tproduit
where noproduit = '&vnoproduit';
select designation into ddesignation
from tproduit
where noproduit = dnoproduit;
\end{verbatim}
Tableaux PL/SQL.
Déclaration :
\begin{verbatim}
DECLARE
...
TYPE tchar4 IS TABLE OF CHAR(4)
INDEX BY BINARY_INTEGER ;
table_noprod tchar4 ;
p BINARY_INTEGER ;
\end{verbatim}
Utilisation :
\begin{verbatim}
BEGIN
...
p := 1 ;
table_noprod(p) := p001 ;
\end{verbatim}
\end{exemple}
\section{Les traitements du bloc \code{BEGIN}}
\subsection{L'ordre SELECT :}
\begin{itemize}
\item Syntaxe :
\begin{verbatim}
SELECT coll, col2
INTO var1, var2
FROM table
[WHERE condition];
\end{verbatim}
\item
Règle :
\begin{itemize}
\item La clause \code{INTO} est {\bf obligatoire}.
\item Le \code{SELECT} doit obligatoirement ramener une ligne
et une seule, sinon erreur. Pour traiter un ordre
\code{SELECT} qui pourrait ramener plusieurs lignes, il faut
utiliser un curseur.
\end{itemize}
\end{itemize}
Les autres ordres de manipulation sont inchangés; des
variables peuvent être utilisées :
\begin{verbatim}
INSERT INTO table VALUES( var1, chaine, 123, var2);
UPDATE table SET col2 = var1 WHERE col1 = var2;
\end{verbatim}
\begin{exercice}
Nombre de fournisseurs dun produit donné : le résultat sera écrit
dans une table \code{ligne}. Les tables utilis\'ees sont donn\'ees
dans la Figure~\ref{fig:bd}.
\end{exercice}
\begin{verbatim}
DROP TABLE tligne ;
CREATE TABLE tligne (ligne varchar2(100));
set echo off ;
set verify off ;
set feed off ;
variable vnoproduit char(4)
prompt Entre le numero du produit:
accept vnoproduit
DECLARE
dnbfournisseur number ;
BEGIN
SELECT count(*) into dnbfournisseur
FROM Tproduitfourn
WHERE noprod ='& vnoproduit';
INSERT INTO tligne VALUES ('NB fournisseur='||to_char(dnbfournisseur)|
|' du produit '|| '&vnoproduit');
END;
.
/
SELECT * FROM tligne ;
set verify on ;
set feed on ;
set echo on ;
\end{verbatim}
\subsection{Traitements conditionnels \code{IF...THEN...END IF;}}
\begin{verbatim}
IF condition1 THEN traitement1;
ELSE traitement2;
END IF;
\end{verbatim}
Les opérateurs utilisés dans les conditions sont les même que dans SQL
:
\verb+ =, <, >, !=, >=, <=, IS NULL, IS NOT NULL, BETWEEN, LIKE, AND, OR, ...+
\begin{exercice}
Nombre de fournisseurs dun produit donné : sil ny a pas de
fournisseur, compter le nombre de produits : il doit être 1 ou
0. \'Ecrire dans une table de lignes soit le nombre de fournisseurs
soit le message \code{le produit nexiste pas}.
\end{exercice}
\begin{verbatim}
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vnoproduit char(4)
prompt Entre le numero du produit:
accept vnoproduit
DECLARE
dnbfournisseur number;
dnbproduit number;
BEGIN
SELECT count(*) into dnbfournisseur
FROM Tproduitfourn
WHERE noprod ='& vnoproduit';
IF dnbfournisseur = THEN
SELECT count(noproduit) into dnbproduit
FROM Tproduit
WHERE noproduit = & vnoproduit';
if dnbproduit = 0 then
INSERT INTO tligne VALUES ('Le produit ' || '&vnoproduit'|| ' n''existe pas');
else
INSERT INTO tligne VALUES ('NB fournisseur='||to_char(dnbfournisseur)|
|' du produit ' || '&vnoproduit');
end if;
END IF;
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\subsection{Traitements répétitifs \code{WHILE ...LOOP ...END LOOP;}}
La boucle \code{WHILE}: L'exécution de la boucle se fait tant
que la condition de la clause de \code{WHILE} est vérifiée.
\begin{verbatim}
BEGIN
WHILE condition
LOOP
instructions;
END LOOP;
END;
\end{verbatim}
La condition est une combinaison d'expressions au moyen d'opérateurs :
\verb+<, >, =, !=, AND, OR, LIKE, ...+
\begin{exemple}Calcul de factorielle.
\begin{verbatim}
CREATE TABLE tligne (ligne VARCHAR2(200));
VARIABLE n NUMBER
PROMPT taper n
ACCEPT n
DECLARE
fn NUMBER := 1;
i NUMBER :=1;
n NUMBER :='&n';
BEGIN
WHILE i<n
LOOP
i := i + 1;
fn := fn * i;
END LOOP;
INSERT INTO tligne
VALUES ( 'Factorielle de '|| TO_CHAR(n)||, ,TO_CHAR(fn));
END;
/
SELECT * FROM tligne;
DROP TABLE tligne;
\end{verbatim}
\end{exemple}
\section{Gestion des erreurs : \code{EXCEPTION ...WHEN ...THEN}}
\subsection{La section \code{EXCEPTION}}
La section \code{EXCEPTION} permet d'affecter un traitement approprié
aux erreurs survenues lors de l'exécution du bloc \plsql{}.
Les types d'erreurs sont les suivants :
\begin{itemize}
\item erreurs Oracle: elles sont prédéfinies
\item erreur programme utilisateur : à déclarer
\end{itemize}
Dans l'exemple ci-dessous, les résultats à afficher sont placés dans
une table \code{tligne}.
\begin{exemple}~
\begin{verbatim}
create table tligne ( ligne varchar2(150));
variable vnoproduit char(4)
prompt taper la référence du produit à rechercher:
accept vnoproduit
declare
dnoproduit char(4);
ddesignation varchar2(30);
dmessage varchar2(150);
begin
dnoproduit:= &vnoproduit;
dmessage := 'Référence inconnue ';
select designation intoddesignation From tproduit where noproduit = dnoproduit;
insert into tligne values ('Désignation: ' || ddesignation);
exception
when no_data_found then
insert into tligne values ( dmessage);
end;
.
/
select * from tligne;
drop table tligne;
\end{verbatim}
\end{exemple}
\begin{remarque} Le message d'erreur est initialisé juste avant la requête SQL qui risque de provoquer l'erreur; la méthode est à utiliser quand plusieurs requêtes SQL sont susceptibles de déclencher la même exception.
\end{remarque}
\begin{exercice}
Enregistrer une livraison pour un produit donné:
\begin{itemize}
\item saisie du numéro de produit et de la quantité livrée,
\item accès au stock du produit: exception \code{référence inconnue},
\item calcul du nouveau stocket mise à jour du stock.
\end{itemize}
\end{exercice}
\begin{verbatim}
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vnoproduit char(4)
variable vquantite number
prompt Entre le numero du produit:
accept vnoproduit
prompt Entre la quantite du produit:
accept vquantite
DECLARE
dstock number;
dnbproduit char(4);
BEGIN
SELECT noproduit, stock into dnoproduit, dstock
FROM Tproduit
WHERE noproduit = '&vnoproduit';
dstock := dstosck + &vquantite;
UPDATE Tproduit set stock = dstock WHERE noproduit = '&vnoproduit';
EXCEPTION
when no_data_found then INSERT INTO tligne VALUES('Reference inconnue')
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\begin{exercice} Enregistrer un nouveau produit dun fournisseur:
\begin{itemize}
\item saisie des références du produit et du fournisseur, du prix
fournisseur,
\item accès au produit pour vérifier quil existe,
\item accès au fournisseur pour vérifier quil existe,
\item accès à la liaison produit-fournisseur pour vérifier quelle nest pas déjà enregistrée,
\item enregistrement de la liaison.
\end{itemize}
\end{exercice}
\begin{verbatim}
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vnoproduit char(4)
variable vreffourn varchar2(4)
variable vprixf number
variable vqta number
prompt Entrer le numero du produit:
accept vnoproduit
prompt Entrer la reference du fournisseur:
accept vreffourn
prompt Entrer le prix du produit:
accept vnprixf
prompt Entrer la quantite du produit:
accept vqta
DECLARE
dnbproduit char(4);
dreffourn char(4);
dcompteur number;
dmessage varchar2(80);
dmessage:='Produit n existe pas';
BEGIN
SELECT noproduit into dnoproduit
FROM Tproduit
WHERE noproduit ='&vnoproduit';
dmessage:= 'Fournisseur n existe pas';
SELECT reffourn into dreffourn
FROM Tfournisseur
WHERE reffourn ='&vreffourn';
dmessage:=' Liaison existe déjà';
SELECT count(*) INTO dcompteur
FROM Tproduitfourn
WHERE noproduit ='&vnoproduit' and reffourn ='&vreffourn';
if dcompteur =1 then INSERT INTO tligne VALUES(dmessage)
else INSERT INTO Tproduitfourn VALUES (dnoproduit, dreffourn,
&vprixf,0,&vqta);
EXCEPTION
when no_data_found then
INSERT INTO tligne VALUES(dmessage);
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
DEUXIEME SOLUTION
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vnoproduit char(4)
variable vreffourn varchar2(4)
variable vprixf number
variable vqta number
prompt Entrer le numero du produit:
accept vnoproduit
prompt Entrer la reference du fournisseur:
accept vreffourn
prompt Entrer le prix du produit:
accept vnprixf
prompt Entrer la quantite du produit:
accept vqta
DECLARE
dnbproduit char(4);
dreffourn char(4);
dcompteur number;
dmessage varchar2(80);
dmessage:='Produit n existe pas';
BEGIN
SELECT noproduit into dnoproduit
FROM Tproduit
WHERE noproduit ='&vnoproduit';
dmessage:= 'Fournisseur n existe pas';
SELECT reffourn into dreffourn
FROM Tfournisseur
WHERE reffourn ='&vreffourn';
dmessage: =' Liaison existe déjà';
INSERT INTO Tproduitfourn VALUES (dnoproduit, dreffourn, &vprixf,0,&vqta);
EXCEPTION
when no_data_found then INSERT INTO tligne VALUES(dmessage);
when dup_val_on_index then INSERT INTO tligne
VALUES(dmessage);
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\subsection{Erreur Oracle}
Dès que l'erreur Oracle est rencontrée, passage automatique à la
section \code{EXCEPTION} pour réaliser le traitement approprié à
l'erreur. Lerreur est documentée par un code derreur \code{SQLCODE}
et un message \code{SQLERRM}.
Syntaxe:
\begin{verbatim}
EXCEPTION
WHEN nom erreur THEN traitement;
[WHEN nom erreur THEN traitement;]
...
[WHEN OTHERS THEN traitement;]
\end{verbatim}
Sortie du bloc après exécution du traitement.
Les principales erreurs Oracle prédéfinies sont d\'ecrires dans le
tableau ci-dessou.
\begin{tabular}{l|l|l}
déclenchée par : & nom de lerreur :& valeur correspondante du
\code{sqlcode}\\
\hline
\code{insert}, \code{update} & \verb+DUP_VAL_ON_INDEX+ & -1\\
& \verb+INVALID_CURSOR+ & -1001 \\
& \verb+INVALID_NUMBER+ & -1722 \\
& \verb+LOGIN_DENIED+ & -1017 \\
\code{select} & \verb+NO_DATA_FOUND+ & +100 (non
déclenché par \code{update} ou \code{delete},\\
& & \phantom{+100 (}ni par \verb+select count+, \code{select sum})\\
& \verb+NO_LOGGED_ON+ & -1012\\
& \verb+PROGRAM_ERROR+ & -6501\\
& \verb+STORAGE_ERROR+ & -6500 \\
& \verb+TIMEOUT_ON_RESOURCE+ & -51 \\
\code{select} & \verb+TOO_MANY_ROWS+ & -1422 \\
&\verb+VALUE_ERROR+ & -6502\\
&\verb+ZERO_DIVIDE+ & -1476\\
& \verb+OTHERS+ & toutes les autres erreurs non
explicitement nommées, \\ && en particuliers celles concernant les
contraintes dintégrité \\ && et les conflits daccès. \code{Others}
doit être la dernière erreur \\ && de la section \code{exception}.\\
\end{tabular}
\begin{exemple}[Détection de doublons sur une clé primaire.]
La séquence suivante crée une ligne dans la table \code{tproduit (noproduit char(4) primary key, ...)} et stocke les erreurs dans une table \code{tligne (ligne varchar2(150));}
\begin{verbatim}
BEGIN
INSERT INTO tproduit VALUES (dnoproduit, ddesignation ...);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
INSERT INTO tligne VALUES (dnoproduit || 'déjà inséré');
END;
\end{verbatim}
\end{exemple}
\begin{exemple} Récupération du message derreurretourné par Oracle en cas
dexception.
Attention : \code{SQLERRM} et \code{SQLCODE} ne peuvent être
utilisés directement dans une instruction SQL. Il faut les récupérer
dans une variable.
\begin{verbatim}
DECLARE
dnom tproduit.designation%type;
dmess1 VARCHAR2(100);
dmess2 VARCHAR2(100);
BEGIN
dmess1 := 'Référence inconnue';
SELECT designation INTO dnom FROM tproduit
WHERE noproduit = &vnoproduit;
EXCEPTION
WHEN OTHERS THEN
dmess2:= SQLERRM;
INSERT INTO tligne VALUES (dmess1);
INSERT INTO tligne VALUES (dmess2);
COMMIT;
END;
\end{verbatim}
\end{exemple}
\begin{exercice} Mise en évidence des exceptions \verb+TOO_MANY_ROWS, OTHERS+
Pour mettre en évidence lexception \verb+TOO_MANY_ROWS+, créez un
fichier de commandes SQL qui: demande un numéro de fournisseur, accède
aux N° des produits de ce fournisseur (par select into) retourne le
message \code{le fournisseur na pas de produit} ou \code{le fournisseur a plusieurs
produits}, ou le N° du produit sil nen a quun.
Le résultat sera placé dans une table \code{TLIGNE (LIGNE
varchar2(200))} Placez aussi dans cette table le \code{SQLCODE} et
le message Oracle obtenus à lissue de \code{SELECT}.
Modifiez le programme en remplaçant une des deux exceptions par \code{OTHERS}.
\end{exercice}
\begin{verbatim}
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vreffourn char(4)
prompt Entrer la reference du fournisseur:
accept vreffourn
DECLARE
dnoproduit char(4);
BEGIN
SELECT noprod INTO dnoproduit
FROM Tproduitfourn
WHERE reffourn ='&reffourn'
INSERT INTO tligne VLAUES (dnoporduit)
EXCEPTION
when no_data_found then
INSERT INTO tligne VALUES(TO_CHAR(SQLCODE)||''||SQLERRM||'
Le fournisseur n''a pas le produit' );
when too_many_rows then
INSERT INTO tligne VALUES(O_CHAR(SQLCODE)||' '||SQLERRM||'
Le fournisseur a plusieurs produit');
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\section{Les curseurs}
\subsection{\code{DECLARE, OPEN, FETCH, CLOSE}}
Lorsquune requête \code{SELECT} est susceptible de délivrer plusieurs
lignes, on associe à celle ci un «curseur explicite» qui va
permettre daccéder aux lignes du résultat. Lalgorithme est analogue
au traitement dun fichier séquentiel.
\begin{exemple}[Traitement des rayons dun étage donné]
\begin{verbatim}
DECLARE
detage trayon.etage%TYPE;
dcode_rayon trayon.code_rayon%TYPE;
dnom_rayon trayon.nom_rayon%TYPE;
CURSOR r IS select code_rayon, nom_rayon from trayon where etage = detage;
BEGIN
detage := 2;
OPEN r;
FETCH r INTOdcode_rayon, dnom_rayon;
WHILE r%FOUND
LOOP
... traitement de la ligne lue ...
FETCH r INTOdcode_rayon,dnom_rayon;
END LOOP;
CLOSE r;
END;
\end{verbatim}
\end{exemple}
\begin{exercice} Pourcentage de produits ayant un stock nul (une solution avec curseur et une sans).
\end{exercice}
\begin{verbatim}
VERSION SANS CURSEUR
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
declare
dtotalproduit number;
dnbproduitstocknull number;
BEGIN
SELECT coutn(*) INTO dtotalproduit
FROM Tproduit;
SELECT coutn(*) INTO dnbproduitstocknull
FROM Tproduit
WHERE stock =0 ;
INSERT INTO tligne VALUES ('Le pourcentage est '||
TO_CHAR(100*(dnbproduitstocknull/dtotalproduit)))
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
VERSION AVEC CURSEUR
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
declare
dtotal number;
dstocknull number;
dstock Tproduit.stock%TYPE;
CURSOR r IS
SELECT stock
FROM Tproduit;
BEGIN
dtotal =0;
dstocknull=0;
OPEN r;
FETCH r INTO dstock;
WHILE r%FOUND
LOOP
dtotal=dtotal +1;
if dstock=0 then tdstocknull= dstocknull+1;
FETCH r INTO dstock;
END LOOP;
CLOSE R;
INSERT INTO tligne VALUES ('Le pourcentage est '|| TO_CHAR(100*(dnbproduitstocknull/dtotalproduit)))
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\begin{exercice} Créer une table des produits classés par valeur du stock: No de produit, désignation, valeur du stock, classement.
\end{exercice}
\begin{verbatim}
DROP TABLE classement;
CREATE TABLE classement (noproduit varchar2(4),designation varchar2(100),
stock number,classement number);
set echo off;
set verify off;
set feed off;
declare
dnoproduit Tproduit.noproduit%TYPE;
ddesignation Tproduit.designation%TYPE;
dstock Tproduit.stock%TYPE;
cpt number;
CURSOR r IS
SELECT noproduit, designation, stock
FROM Tproduit
ORDER by stock;
BEGIN
cpt: =0;
open r;
FETCH r INTO dnoproduit, ddesignation, dstock;
WHILE r%FOUND
LOOP
cpt:= cpt +1;
INSERT INTO classement VALUES (dnoproduit,ddesigantion,
TO_CHAR(dstock), TO_CHAR(cpt));
FETCH r INTO dnoproduit, ddesignation, dstock;
END LOOP;
CLOSE R;
END;
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\begin{exercice} créer une table des produits qui nont pas de fournisseur et une table des produits qui en ont avec les noms de leurs fournisseurs.
\end{exercice}
\begin{verbatim}
DROP TABLE produitsansfourn;
CREATE TABLE produitsansfourn (noproduit varchar2(4));
DROP TABLE produitfourn;
CREATE TABLE produitfourn (noproduit varchar2(4),nomfournisseur varchar2(4));
set echo off;
set verify off;
set feed off;
declare
dnoproduit Tproduit.fourn%TYPE;
dnomfourn Tfournisseur.nom%TYPE;
CURSOR NF IS
SELECT noproduit FROM Tproduit
WHERE noproduit not in (SELECT noproduit from Tproduitfourn);
CURSOR r IS
SELECT Tproduit.noproduit, Tfournisseur.nom
FROM Tproduit, Tproduitfourn, Tfournisseur
WHERE Tproduit.noproduit=Tproduitfourn.noproduit and
Tproduit.reffourn = Tfournisseur.reffourn;
BEGIN
open r;
FETCH r INTO dnoproduit;
WHILE r%FOUND
LOOP
INSERT INTO produitfourn VALUES (dnoproduit);
FETCH r INTO dnoproduit;
END LOOP;
CLOSE R;
open NF;
FETCH NF INTO dnoproduit;
WHILE NF%FOUND
LOOP
INSERT INTO produitsansfourn VALUES (dnoproduit);
FETCH NF INTO dnoproduit;
END LOOP;
CLOSE NF;
END;
.
/
set verify on;
set feed on;
set echo on;
\end{verbatim}
\subsection{\code{UPDATE, DELETE ... WHERE CURRENT OF curseur}}
Lorsque lon parcourt une table (ou une vue modifiable) par
\code{FETCH} à laide dun curseur, on peut demander à mettre à jour
ou supprimer la ligne courante par la condition \code{WHERE CURRENT
OF} nom du curseur. Le curseur doit avoir été déclaré \code{FOR
UPDATE}
\begin{verbatim}
CURSOR r IS
select code_rayon, nom_rayon from trayon where etage = detage FOR UPDATE;
\end{verbatim}
La mise à jour se fera par :
\begin{verbatim}
UPDATE trayon SET nom_rayon = dnom_rayon WHERE CURRENT OF r;
COMMIT;
\end{verbatim}
Attention, terminer par \code{COMMIT}, sinon risque dinterblocage
entre deux exécutions qui voudraient écrire dans la même table.
\begin{exercice}
augmenter de 10\% les produits de plus de 1000\euro{}, et de 5\% les autres.
\end{exercice}
\begin{verbatim}
set echo off;
set verify off;
set feed off;
declare
dprix Tproduit.prix%TYPE;
CURSOR r IS
SELECT prixv
FROM Tproduit
FOR UPDATE;
BEGIN
open r;
FETCH r INTO dprix;
WHILE r%FOUND
LOOP
if dprix>1000
then UPDATE Tproduit SET prixv=dprix*1.1 WHERE CURRENT of r; COMMIT;
else UPDATE Tproduit SET prixv=dprix*1.05 WHERE CURRENT of r; COMMIT;
end if;
FETCH r INTO dprixv;
END LOOP;
CLOSE R;
END;
.
/
set verify on;
set feed on;
set echo on;
\end{verbatim}
Erreur utilisateur : \code{EXCEPTION, RAISE}
Le traitement de lanomalie doit être déclenché en passant dans la
partie \code{EXCEPTION} par \code{RAISE}.
\begin{verbatim}
DECLARE
...
nom_erreur EXCEPTION;
...
BEGIN
...
IF anomalie
THEN RAISE nom_erreur;
...
EXCEPTION
WHEN nom_erreur THEN
traitement à effectuer en cas danomalie;
END ;
\end{verbatim}
Sortie du bloc après exécution du traitement.
\begin{exemple}
Accèder au stock dun produit de Numéro donné. Erreur Oracle quand le
produit nexiste pas Erreur utilisateur quand le stock est nul Les
anomalies sont placées dans une table \code{terreur(z1 varchar2(30),
z2 varchar2(30))}
\begin{verbatim}
DECLARE
...
stock_nul EXCEPTION;
dstock number;
...
BEGIN
SELECT stock INTO dstock FROM tproduit WHERE noproduit = &vnoproduit;
IF stock = 0
THEN RAISE stock_nul;
traitement quand le stock nest pas nul...
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO terreur VALUES (&vnoproduit, produit inconnu);
WHEN stock_nul THEN
INSERT INTO terreur VALUES (&vnoproduit, stock nul);
END;
\end{verbatim}
\end{exemple}
\begin{exercice} Pour remplacer lerreur Oracle par une erreur utilisateur, compter le nombre de produits ayant le numéro donné et déclencher une exception si ce nombre est nul.
\end{exercice}
\begin{verbatim}
DROP TABLE tligne;
CREATE TABLE tligne (ligne varchar2(100));
set echo off;
set verify off;
set feed off;
variable vnoproduit varchar2(4)
prompt Entrer le numereo du produit:
accept vnoproduit
DECLARE
nbproduitnull EXCEPTION;
dnbproduit number;
BEGIN
SELECT count(*) INTO dnbproduit
FROM Tproduit
WHERE noproduit ='&vnoproduit'
if dnobproduit =0 the raise nbproduitnull;
end if;
EXCEPTION
when nbproduitnull then
INSERT INTO tligne VLAUES ('&vnoporduit)'||'n existe pas');
.
/
SELECT * FROM tligne;
set verify on;
set feed on;
set echo on;
\end{verbatim}
\newpage
\begin{figure}[htb]
\begin{center}
\includegraphics[width=18cm]{BD.png}
\end{center}
\caption{Base de donn\'ees pour les exercices.}\label{fig:bd}
\end{figure}
\newpage
\begin{center}
{\bf Commandes utiles pour les TP}
\end{center}
\paragraph{Premi\`ere s\'eance :} Connexion : \verb+sqlplus <user>/<password>@KIROV+
\verb+source /etc/profile+
Lancer \verb+sqlplus+ avec cette commande \verb+rlwrap sqlplus+ vous
permet d'avoir l'historique.
Lors de votre première connexion modifier votre mot de passe avec la
commande SQL : \verb+PASSWORD;+
Ce qui est équivalent à :
\verb+ALTER USER dupond IDENTIFIED BY password;+
Si vous avez perdu votre mot de passe se connecter en \code{ssh} sur
\code{londres} et faire \code{oracle\_passwd}
Pour lancer un fichier \verb+.sql+ en sqlplus, il suffit de taper :
\verb+@toto.sql;+
Pour quitter sqlplus, il suffit de taper : \verb+quit;+
\paragraph{Mise en forme : }
Sous sqlplus :
\begin{verbatim}
Set linesize 150 -- positionne la taille d'une ligne
Set pagesize 300 -- positionne le nombre de lignes avant de réafficher les entêtes
Set pages 0 -- n'affiche pas les entêtes
Col <nom_colonne> for A10 -- défini que la colonne nom_colonne va être affiché sur
10 caractères alphanumériques, 999.99 pour les valeurs numériques.
\end{verbatim}
\paragraph{Corbeille :} Vider les tables \verb+BIN$$xxxx+ :
\verb+PURGE RECYCLEBIN;+
\paragraph{D\'ebug :} Connaître l'utilisateur connect\'e : \verb+show user;+
Liste des tables d'un \verb+user+ : \verb+SELECT table_name FROM user_tables;+
Lister les tables accessibles par l'utilisateur
:\verb+ SELECT table_name FROM all_tables;+
Lister toutes les tables possédées par un utilisateur
\verb+SELECT * FROM all_tables WHERE owner='PALAFOUR';+
Liste des vues d'un user : \verb+SELECT view_name FROM user_views;+
Liste des contraintes : \verb+SELECT * FROM user_constraints WHERE table_name=<table>;+
\verb+SELECT * FROM user_cons_columns WHERE table_name='<table>";+
\paragraph{Contraintes :} Description d'une table : \verb+describe <table>+ ou
\verb+desc <table>+
Liste des colonnes concernées par les contraintes :
\verb+SELECT * FROM user_cons_columns;+
Lire une table d'un autre schéma :
\verb+SELECT * FROM <schema>.<table_name>; -- ou schma = login de connexion de l'utilisateur+
\paragraph{Affichage :} ~ \\
\verb+SET HEADING OFF+
\verb+SET FEEDBACK OFF+
\paragraph{Acc\`es fichiers}
\begin{verbatim}
https://homeweb.iut-clermont.uca.fr/pydio/
\end{verbatim}
\paragraph{Aide en ligne}
\begin{verbatim}
http://cr2i.intranet.iut.local/site/
\end{verbatim}
%\newpage
%\printindex
\end{document}