\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}

%% L’objectif 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
d’un produit et l’affichage de sa désignation ou d’un  message
d’erreur si le N° de produit n’existe 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 d’instruction de saisie ou d’affichage. Les
commandes \code{SET} positionnent des variables d’environnement pour
éviter que le système n’affiche des informations « polluant » le
résultat :
\begin{itemize}
\item \code{set echo off} évite que le système n’affiche la commande
  du bloc PL/SQL au moment où il l’exécute,
\item \code{set verify off} évite que le système n’affiche l’opération
  de substitution au moment où il la fait,
\item \code{set feed off} évite que le système n’affiche 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 l’exé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 d’oubli, 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
%%   l’environnement 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 d’un 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 d’un produit donné : s’il n’y 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 n’existe 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 d’un 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 qu’il existe,
\item accès au fournisseur pour vérifier qu’il existe,
\item accès à la liaison produit-fournisseur pour vérifier qu’elle n’est 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.  L’erreur est documentée par un code d’erreur \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 l’erreur :& 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 d’intégrité \\ && et les conflits d’accè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 d’erreurretourné par Oracle en cas
  d’exception.
  
  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 l’exception \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 n’a pas de produit}’ ou ‘\code{le fournisseur a plusieurs
produits}’, ou le N° du produit s’il n’en a qu’un.

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 à l’issue 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}}

Lorsqu’une requête \code{SELECT} est susceptible de délivrer plusieurs
lignes, on associe à celle ci un «curseur explicite» qui va
permettre d’accéder aux lignes du résultat. L’algorithme est analogue
au traitement d’un fichier séquentiel.

\begin{exemple}[Traitement des rayons d’un é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 n’ont 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 l’on parcourt une table (ou une vue modifiable) par
\code{FETCH} à l’aide d’un 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 d’interblocage
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 l’anomalie 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 d’anomalie;
END ;
\end{verbatim}

Sortie du bloc après exécution du traitement.

\begin{exemple}
Accèder au stock d’un produit de Numéro donné.  Erreur Oracle quand le
produit n’existe 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 n’est 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 l’erreur 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}
