<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "/usr/share/xml/docbook/4.2/docbookx.dtd">
<article lang="fr" >

  <!--  ================================= Entetes info ============================= -->

  <articleinfo>
    <author>
      <firstname>Ivan</firstname>
      <surname>Kurzweg</surname>  
      <email>ik-r@wanadoo.fr</email>
    </author>

    <title>Modélisation des données</title>

    <copyright>
      <year>2007</year>
      <holder>Ivan KURZWEG, <emphasis>ivan.kurzweg@wanadoo.fr</emphasis></holder>
    </copyright>

    <legalnotice>
      <para>Permission to use, copy, modify, and distribute this documentation for any purpose with or without fee is here by granted, provided that the above copyright notice and this permission notice appear in all copies.
</para>        
    </legalnotice>

    <keywordset>
      <keyword></keyword>
    </keywordset>

  </articleinfo>

  <abstract> 
    <para>Ce document propose une  approche orientée "développement d'application" du SGBDR MySQL. Y sont développés les points suivants : 
<orderedlist>
	  <listitem>
	    <para><emphasis>Création de base sous MySQL</emphasis> : types de base, de tables, de données, clefs, index, requêtes SQL;</para></listitem>
	  <listitem>
	    <para><emphasis>Manipulation de données sous MySQL</emphasis> : requêtes de sélection, d'insertion/modification/suppression d'enregistrements;</para></listitem>
	  <listitem>
	    <para><emphasis>Optimisation et sécurisation de MySQL </emphasis>: optimisation, sauvegarde, restauration, outils complémentaire;</para></listitem></orderedlist>
</para>
<para>
Les exemples du cours ont été réalisés dans un environnement FreeBSD. Tous les exemples sont proposés à partir de l'outil en ligne de commande <filename>mysql</filename>, mais, pour des raisons de lisibilité, les présentations en cours se feront via les interfaces graphiques MySQL GUI Tools.
</para> 

      <para>Les exercices d'applications du cours sont à réaliser sur la base de données proposée sur le site <ulink url="http://sqlpro.developpez.com/cours/bddexemple/">sqlpro.developpez.com</ulink> Fr~d~ric Brouard. Je vous conseille particulièrement la lecture de ce site, qui éclairera certains points du cours, et donnera une vision plus "générale" des possibilités des SGBDRs.</para></abstract>

<!-- =============================== Chapitre 1 ================================= -->

  <sect1>
   <title>Du  modèle relationnel à la base de données</title>
  <abstract>  <para>
Dans ce chapitre, après une courte introduction à MySQL,  nous allons examiner dans un premier temps les différents types de données que nous propose MySQL, puis les différentes étapes nécessaires à la création d'une base de données MySQL.
</para>  </abstract>  
    <sect2>
	<title>MySQL, un Système de Gestion de Bases de Données Relationnelles (<acronym>SGBDR</acronym>)</title>
	<sect3>
	  <title>SGBDs</title>
	<para>Une base de données est une <emphasis>entité</emphasis> dans laquelle il est possible de stocker des données de façon structurée et avec le moins de redondance possible. Ces données doivent pouvoir être utilisées par des programmes, par des utilisateurs <emphasis>différents</emphasis>. Ainsi, la notion de base de données est généralement couplée à celle de réseau, afin de pouvoir mettre en commun ces informations, d'o~ le nom de base. </para>

      <para>Le concept permet de stocker et d'organiser une grande quantité d'information. Les SGBD (Système de Gestion de Base de Données) permettent de naviguer dans ces données et d'extraire (ou de mettre à jour) les informations voulues au moyen d'une requête.</para>

	<para>Les données apparaissent comme stockées dans des <emphasis>tables</emphasis> qu'on peut mettre en relation. Une table elle-même est une relation, mais entre les différents champs qui la composent.</para>

	<para>Ce système se démarque donc totalement en termes d'interface des bases de données de type hiérarchique, même si au plan de l'implémentation, et en fonction des statistiques d'accès à la base, un modèle hiérarchique sera utilisé, qui n'aura jamais besoin d'être pris en compte par l'utilisateur. De plus les données d'une table sont souvent subordonnées à un des champs (une <emphasis>clé</emphasis>).</para>
	<para>
Ce modèle relationnel conduit à :

<itemizedlist>
	    <listitem>
	      <para> une grande <emphasis>simplicité</emphasis> d'usage</para></listitem>
     <listitem>
	      <para>  une <emphasis>transparence</emphasis> pour l'utilisateur de toute réorganisation technique de la base (la seule différence pour l'utilisateur se situera, si l'opération est réussie, dans les temps de réponse).</para></listitem>
     <listitem>
	      <para> une facilité de c<emphasis>ombinaison du contenu de plusieurs tables</emphasis> (opération join ou jointure).</para></listitem></itemizedlist></para>
	<para>
Les tables possèdent un certain nombre de colonnes ou champs permettant de décrire des n-uplets (lignes ou enregistrements). La non-duplication (absence de redondance) des n-uplets est théoriquement assurée par le SGBDR.</para>

	<para>Dans les relations, nous avons vu qu'il est possible de définir deux types de clés :

<orderedlist>
   <listitem>
	      <para><emphasis>clé primaire</emphasis> :  permet d'identifier un et un seul n-uplet (par exemple le numéro de sécurité sociale).</para></listitem>
   <listitem>
	      <para><emphasis>clé étrangère</emphasis> : c'est un attribut d'une relation qui est clé primaire dans une autre relation. Elle permet donc de lier deux tables entre elles.</para></listitem></orderedlist></para>

	<para>Pour accéder aux données, on utilise différents opérateurs logiques, notamment la sélection (ou projection), mais aussi les jointures (dont il existe différents types). Les opérations sont communiquées sous forme de requêtes aux SGBDR (Système de Gestion de Base de Données Relationnelle). La plupart utilisent le langage normé <acronym>SQL</acronym>.
</para>	<para>
Dans une base de données relationnelle, le but est de séparer les informations au maximum pour éviter les doublons et la redondance, et d'empêcher la perte de qualité d'information (par exemple, l'adresse d'un fournisseur n'est mise à jour qu'une et une seule fois : la modification sera alors prise en compte sur l'ensemble des courriers). Cette bonne organisation des données nous est normalement garanti si le Modèle relationnel conçu est valide ...</para></sect3>

      <sect3>
        <title>Présentation de MySQL</title>

        <para>MySQL est créé en 1995 par une société suédoise. Il est aujourd'hui développé et distribué par la société commerciale MySQL AB. </para>

        <para>MySQL est passé en Open Source (GPL) depuis sa version 3.3, mais il est également proposé en version licencié.</para>

        <para>MySQL AB fournit différents types de logiciels :

<itemizedlist>
	      <listitem>
		<para>      Le serveur MYSQL et les scripts de démarrage :
            <itemizedlist>
		    <listitem>
		      <para>     <command>mysqld</command>  est le serveur MySQL </para></listitem>
		    <listitem>
		      <para>          <command> mysqld_safe</command> ,  <command>mysql.server</command> et  <command>mysqld_multi</command> sont les scripts de démarrage. </para></listitem>
		    <listitem>
		      <para>          <command>mysql_install_db</command>  initialise le dossier de données et les premi~res bases.</para></listitem></itemizedlist> </para>
	      </listitem>


	      <listitem>
		  <para>		     Logiciels clients pour accéder au serveur :
             <itemizedlist>
		      <listitem>
			<para>        <command>mysql</command> est un client en ligne de commande, pour éxécuter des commandes SQL, interactivement, ou en mode batch. </para></listitem>
		      <listitem>
			<para>            <command>mysql-query-browser </command> est un client interactif graphique, pour exécuter des commandes SQLadministrer le serveur </para></listitem><listitem>
			<para>            <command>mysql-admin</command> est un client interactif graphique pour administrer le serveur </para></listitem>
		      <listitem>
			<para>                  <command>mysqladmin</command> est un client d'administration </para></listitem>
		      <listitem>
			<para>                    <command>mysqlcheck</command> effectue les opérations de maintenance sur les tables </para></listitem>
		      <listitem>
			<para>                    <command>mysqldump</command> etmysqlhotcopy font les sauvegardes de bases </para></listitem>
		      <listitem>
			<para>                     <command>mysqlimport</command> importe des fichiers de données </para></listitem>
		      <listitem>
			<para>                   <command>mysqlshow</command> affiche des informations sur les bases et les tables </para></listitem>
		    </itemizedlist></para></listitem>

		<listitem>
		  <para>      Utilitaires qui fonctionnent indépendamment du serveur :
                 <itemizedlist>
		      <listitem>
			<para>       <command>myisamchk</command> effectue les opérations de maintenance des tables </para></listitem>
		      <listitem>
			<para>                   <command>myisampack</command> produit des tables compressées, en lecture seule </para></listitem>
		      <listitem>
			<para>                 <command>mysqlbinlog</command> est un outil pour traiter les fichiers de logs binaires </para></listitem>
		      <listitem>
			<para>                 <command>perror</command> affiche le message associé ~ un code d'erreur</para></listitem></itemizedlist></para>
</listitem>
 </itemizedlist>
	</para>

<para>Extrait du manuel de référence de MySQL : </para>
	  <itemizedlist>
	    <listitem>
	      <para><emphasis>MySQL est un serveur de bases de données
        relationnel.</emphasis>Un serveur de bases de données
        stocke les données dans des tables séparées plutà´t que de tout
        rassembler dans une seule table. Cela améliore la rapidité et la
        souplesse de l'ensemble. Les tables sont reliées par des relations
        définies, qui rendent possible la combinaison de données entre
        plusieurs tables durant une requête. Le SQL dans `` MySQL '' signifie
        `` Structured Query Language '' : le langage standard pour les
        traitements de bases de données.</para></listitem>

	    <listitem>
	      <para><emphasis>MySQL est Open Source</emphasis> . Open
        Source (Standard Ouvert) signifie qu'il est possible à  chacun
        d'utiliser et de modifier le logiciel. Tout le monde peut télécharger
        MySQL sur Internet, et l'utiliser sans payer aucun droit. Toute
        personne en ayant la volonté peut étudier et modifier le code source
        pour l'adapter à  ses besoins propres. Le logiciel MySQL utilise la
        licence GPL ( GNU General Public License ), pour définir ce que vous
        pouvez et ne pouvez pas faire avec ce logiciel, dans différentes
        situations. Si vous ne vous sentez pas confortable avec la licence GPL
        ou bien que vous devez intégrer MySQL dans une application
        commerciale, vous pouvez acheter une licence commercial auprès de
        MySQL AB. Licences MySQL . </para></listitem>

	    <listitem>
        <para><emphasis>Pourquoi utiliser le serveur de bases de données MySQL
        ?</emphasis>Le serveur de bases de données MySQL est très
        rapide, fiable et facile à  utiliser. Si c'est ce que vous recherchez,
        vous devriez faire un essai. Le serveur de bases de données MySQL
        dispose aussi de fonctionnalités pratiques, développées en coopération
        avec nos utilisateurs. Vous pouvez trouver une comparaison des
        performances du serveur MySQL avec d'autres systèmes de bases de
        données dans nos pages de tests de performances. La suite de tests
        comparatifs de MySQL .Le serveur MySQL a été développé à  l'origine
        pour gérer de grandes bases de données plus rapidement que les
        solutions existantes, et a été utilisé avec succès dans des
        environnements de production très contraints et très exigeants, depuis
        plusieurs années. Bien que toujours en développement, le Le serveur
        MySQL offre des fonctions nombreuses et puissantes. Ses possibilités
        de connexions, sa rapidité et sa sécurité font du serveur MySQL une
        serveur hautement adapté à  Internet. </para></listitem>

	    <listitem>
        <para><emphasis>Les caractéristiques techniques du serveur MySQL
        .</emphasis>Le logiciel de bases de données MySQL est un
        système client/serveur, constitué d'un serveur SQL multi-threadé qui
        supporte différents systèmes de stockage, plusieurs logiciels clients
        et librairies, outils d'administration, ainsi que de nombreuses
        interfaces de programmation (des API ).Nous fournissons aussi le
        serveur MySQL sous la forme d'une librairie multi-threadé que vous
        pouvez inclure dans vos applications, pour obtenir un produit plus
        compact, plus rapide et plus facile à  gérer. </para></listitem></itemizedlist>


      </sect3>

    </sect2>
      <sect2>
	<title>Connexion au serveur et création d'une base de données</title>
	<para>Toutes les manipulations sur les bases de données, que ce soit en terme d'administration ou d'exploitation, se feront via une <emphasis>connexion</emphasis> au serveur MySQL, qui peut être hébergé ou distant. Dans le cadre de ce cours, il sera proposé deux méthodes de travail : via les outils GUI ou via la ligne de commande.</para>
	<sect3>
	  <title>Connexion au serveur MySQL</title>
	  <para>Nous verrons dans la partie consacré aux privilèges que les autorisations de connexions au serveur sont définies en fonction du couple <foreignphrase>login/password</foreignphrase> des utilisateurs MySQL, mais aussi en fonction du poste appelant. Néanmoins, vous devriez pouvoir vous connecter sur votre environnement de développement en utilisant les comptes par défaut (<command>root</command>, que nous n'avons pas encore sécurisé).</para>
<para>La création d'une base de données va nécessiter l'exécution d'une requête sur le serveur, nous allons devoir nous y connecter, soit en utilisant l'outil graphique mysql-query-browser soit en ligne de commande avec mysql. </para>
	  <example>
	    <title>Connexion au serveur </title>

<programlisting>
<![CDATA[
ikare@kaitan:~$ mysql -h arrakis -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 5.1.17-beta FreeBSD port: mysql-server-5.1.17
mysql> 
]]>
</programlisting>
	  </example>
	</sect3>
	<sect3>
	  <title>Création d'une base de données</title>
<para>Créer une base de données sous MySQL se fait via une requête normalisée SQL. Lors de la création, MySQL effetue les opérations suivantes : 
<itemizedlist>
	      <listitem>
	      <para>Création des répertoires qui stockeront les fichiers de données (données, index, etc..)</para></listitem>
	      <listitem>
	      <para>Mise à jour de la base information_schema, qui contient les options des différentes bases de données hébergées</para></listitem></itemizedlist>
</para>
	  <para>La requête de création de base <command>CREATE DATABASE</command> est la suivante : 
<informalexample>
<programlisting>
CREATE DATABASE [IF NOT EXISTS] db_name
    [create_specification [, create_specification] ...]

create_specification:
    [DEFAULT] CHARACTER SET charset_name
  | [DEFAULT] COLLATE collation_name</programlisting></informalexample>
Les options permettent de ne ne créer la base de données que si elle n'existe pas (sinon, la requête retourne une erreur en cas de conflit), et de préciser les jeux de caractères utilisés dans les tables. Par défault, il s'agit de <filename>latin1</filename>, qui représente l'encodage <filename>ISO 8859-1 West European</filename>. La collation (les règles qui régissent les méthodes de comparaison et de classement des caractères) qui y est associée est <filename>latin1_swedish_ci</filename>.
</para>
	  <para>Tout au long de ce cours, nous allons travailler sur un exemple de base de données, relatif à la gestion d'un bibliothèque personnelle. Nous allons donc créer la base de données <filename>bibliotheque</filename>.</para>
	  <example>
	    <title>Création de la base biblotheque</title>
<programlisting>
<![CDATA[
mysql> CREATE DATABASE bibliotheque;
Query OK, 1 row affected (0.00 sec)
]]>
</programlisting>
	  </example>
<para>Dans ce premier exemple de requête SQL, on peut noter l'emploi de convention de nommage de la base (pas d'accents, de caractères spéciaux, d'espace, etc..), et un premier apperçu de la structure du langage SQL, avec en particulier le point- virgule qui termine chaque instructions du langage.</para>
	  <para>La liste des bases de données créées sur le serveur peut être obtenues en utilisant la commande <command>SHOW DATABASES</command>  : 
<informalexample>
<programlisting>
<![CDATA[mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema | 
| bibliotheque       | 
| database           | 
| location_planche   | 
| mysql              | 
| test               | 
+--------------------+
6 rows in set (0.00 sec)
]]>
</programlisting></informalexample>
	 
</para>
	</sect3>

	<sect3>
	  <title>Sélection de la base de données en cours</title>
	  <para>Le serveur MySQL pouvant héberger plusieurs bases de données, il est nécessaire avant toute manipulation de données de sélectionner la base de travail, opération réalisée par l'instruction <command>USE</command> : 

<programlisting>
<![CDATA[
mysql> mysql> USE bibliotheque
Database changed
]]>
</programlisting>
 </para>
	</sect3>

      </sect2>
      <sect2>
	<title>Types de données dans MySQL</title>
<para>Nous avons vu dans le cours sur la modélisation des données que nous devons typer les données stockées dans le système d'informations. Si nous sommes restés jusque là relativement éloigné de l'implémentation physique des données, nous allons devoir maintenant choisir avec précision les types de données utilisés dans les tables</para>
      

          <para><emphasis role="bold">MySQL</emphasis> supporte un grand
          nombre de types de colonnes, qui peuvent être rassemblés en trois
          groupes : les nombres, les dates et les chaînes de caractères. Les
          types de données de MySQL sont listés ci-dessous. Les codes suivants
          sont utilisés pour préciser les modes d'affichages :</para>

          <itemizedlist>
            <listitem>
              <para><emphasis role="bold">M </emphasis>: Indique la taille
              maximale d'affichage. Le maximum légal est 255.</para>
            </listitem>

            <listitem>
              <para><emphasis role="bold">D</emphasis> : S'applique aux
              nombres à  virgule flottante, et indique le nombre de décimales
              qui suivent la virgule. Le nombre maximum est de 30, mais ne
              doit pas être plus grand que M -2.</para>
            </listitem>

            <listitem>
              <para><emphasis role="bold">UNSIGNED</emphasis> : Indique si la
              propriété optionnelle indiquant si un type de données est
              toujours positif est autorisée. Si <emphasis>UNSIGNED</emphasis>
              est présent, le nombre est toujours positif.</para>
            </listitem>

            <listitem>
              <para><emphasis role="bold">ZEROFILL</emphasis> : la présence de
              cette propriété optionnelle indique que des zéros doivent être
              placés comme caractère de remplissage. Par exemple, pour un type
             <filename>INT(5) ZEROFILL</filename>, la valeur <filename>4</filename>
              sera lue<filename> 0004</filename>.</para>
            </listitem>
          </itemizedlist>

          <sect3>
            <title>Types numériques</title>

            <para>Le tableau ci-dessous cite les différents types de données
            numériques sous MySQL. M indique la taille d'affichage, et D
            (valable pour les nombres en virgule flottante), représente le
            nombre de chiffres après la virgule.</para>

            <table>
              <title>Types numériques sous MySQL</title>

              <tgroup cols="3">
                <tbody>
                  <row>
                    <entry><emphasis role="bold">Type</emphasis></entry>

                    <entry><emphasis role="bold">Borne inférieure</emphasis></entry>

                    <entry><emphasis role="bold">Borne supérieure</emphasis></entry>
                  </row>

                  <row>
                    <entry><filename>TINYINT</filename></entry>

                    <entry>-128 (unsigned : 0)</entry>

                    <entry>127 (unsigned : 255 )</entry>
                  </row>

                  <row>
                    <entry><filename>SMALLINT</filename></entry>

                    <entry>-32768  (unsigned : 0)</entry>

                    <entry>32767 (unsigned : 65535)</entry>
                  </row>

                  <row>
                    <entry><filename>MEDIUMINT</filename></entry>

                    <entry>-8388608 (unsigned : 0)</entry>

                    <entry>8388607 (unsigned : 16777215)</entry>
                  </row>

                  <row>
                    <entry><filename>INT</filename></entry>

                    <entry>-2147483648 (unsigned : 0)</entry>

                    <entry>2147483647 (unsigned : 4294967295)</entry>
                  </row>

                  <row>
                    <entry><filename>BIGINT</filename></entry>

                    <entry>-9223372036854775808 (unsigned : 0)</entry>

                    <entry>9223372036854775807 (unsigned : 18446744073709551615)</entry>
                  </row>

                  <row>
                    <entry><filename>FLOAT</filename></entry>

                    <entry>-3.402823466E+38</entry>

                    <entry>3.402823466E+38</entry>
                  </row>

                  <row>
                    <entry><filename>DOUBLE</filename></entry>

                    <entry>-1.7976931348623157E+308</entry>

                    <entry>1.7976931348623157E+308</entry>
                  </row>
                </tbody>
              </tgroup>
            </table>
          </sect3>

          <sect3>
            <title>Types Date et heure</title>

            <itemizedlist>
              <listitem>
	      <para>Type <filename>TIME</filename> : MySQL lit et affiche les
                colonnes de type <filename>TIME</filename> au format
                'HH:MM:SS'. Les valeurs TIME non valides sont transformées en
                date zéro '00:00:00'</para>
              </listitem>

              <listitem>
	      <para>Type <filename>DATE</filename> : Le type
                <filename>DATE</filename> est prévu lorsque vous souhaitez
                stocker une date. MySQL affiche les valeurs de type
                <filename>DATE</filename> au format '
                <filename>AAAA-MM-JJ</filename> '. L'intervalle de validité va
                de '1000-01-01' à  '9999-12-31'. Les dates non valides sont
                transformées en "0000-00-00"</para>
              </listitem>

              <listitem>
	      <para>Type <filename>DATETIME</filename> : Le type
                <filename>DATETIME</filename> est prévu lorsque vous souhaitez
                stocker une date et une heure. MySQL affiche les valeurs de
                type <filename>DATETIME</filename> au format '
                <filename>AAAA-MM-
JJ HH:MM:SS</filename>'</para>
              </listitem>

              <listitem>
	      <para>Type <filename>TIMESTAMP</filename> : Le type
                <filename>TIMESTAMP</filename> est prévu pour stocker
                automatiquement l'heure courante lors d'une commande
                <filename>INSERT</filename> ou <filename>UPDATE</filename> .
                Si vous avez plusieurs colonnes de type
                <filename>TIMESTAMP</filename> , seule la première colonne
                sera mise à  jour automatiquement.</para>
              </listitem>
            </itemizedlist>
          </sect3>
       

        <sect3>
          <title>Chaînes de caractères</title>

	  <itemizedlist>
	    <listitem>
	      <para><filename>CHAR(L)</filename> : dans ce cas, quelque soit la
          longueur effective de la chaîne, elle sera toujours stockés sur un
          nombre d'octets fixe, L. Par exemple, si vous définissez
          CHAR (10) et que vous entrez la chaîne "Bonjour",
          elle sera complétée par trois espace dans la table. </para></listitem>

	    <listitem>
	      <para><filename>CHAR(L) BINARY</filename>: identique  mais insensible à la casse  lors de tris et des recherches</para></listitem>
	   
	    <listitem>       <para><filename>VARCHAR(L)</filename> : dans ce cas, MySQL stocke la
          chaîne, plus un octet définissant la longueur effecctive de
          l'enregistrement. Dans notre exemple "bonjour", cette chaîne
          occupera 7+1 octets dans la base.</para>
	    </listitem>

	    <listitem>
	      <para><filename>TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT </filename>: ces types de données peut en fait être considéré comme un VARCHAR, mais dont les tailles maximales respectives sont : 225, 65535, 16777215, 4294967295</para></listitem>
	  

	    <listitem>
	      <para><filename>TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB </filename>: ces types de données peut en fait être considéré comme un VARCHAR BINARY, et ainsi contenir des données binaires dont tailles maximales respectives sont : 225, 65535, 16777215, 4294967295</para></listitem>
	  </itemizedlist>

        </sect3>

  <sect3>
          <title>Enumérations</title>
	  <para>Un attribut de type <filename>ENUM</filename> peut prendre une valeur parmi celles déinies lors de la crétion de la table plus la chaîe vide ainsi que <varname>NULL</varname> si la déinition le permet. Ces valeurs sont exclusivement des chaînes de caract~res. Une énumération peut contenir 65535 éléments au maximum. </para>

	  <para>La définition d'un tel attribut se fait de la manière suivante : 
<informalexample>
<programlisting>nom_attribut ENUM("valeur 1","valeur 2", ...)
nom_attribut ENUM("valeur 1","valeur 2", ...) NULL</programlisting></informalexample>
</para>

	  <para>A chaque valeur est associée un index allant de 0 à N si N valeurs ont été définies. L'index 0 est associé à la chaîne nulle, l'index 1 à la première valeur, lindex NULL est associé à la valeur NULL.
</para>


        </sect3>
<sect3>
          <title>Ensembles</title>
	  <para>Un attribut de type <filename>SET</filename> peut prendre pour valeur la chaîne vide, <varname>NULL</varname> ou une chaîne contenant une liste de valeurs qui doivent être déclarées lors de la définition de l'attribut. Il ne peut être défini que 64 éléments maximum.</para>

	  <para>Par exemple, un attribut déclaré comme suit :
<informalexample>
<programlisting>
SET("dvd",vhs","divx")
</programlisting></informalexample>
peut prendre les valeurs suivantes :
<informalexample>
<programlisting>
"" (chaîne vide)
"dvd,divx"
"dvd",vhs","divx"</programlisting></informalexample>
et autres combinaisons de listes des trois valeurs définie plus haut, avec en plus la valeur NULL.</para>



        </sect3>
      </sect2>
      <sect2>
	<title>Création de table</title>
	<sect3>
	  <title>La base de données exemple</title>
<para>La base de données que l'on a créé dans le chapitre précédent relative à la gestion d'une bibliothèque personnelle doit reprendre le modèle relationnel suivant : 
<informalexample>
<programlisting>
LIVRE(_LIV_NUM_, LIV_TITRE, LIV_SS_TITRE, #COL_NUM)
COLLECTION(_COL_NUM_, COL_NOM, COL_EDITEUR)
AUTEUR(_AUT_NUM_, AUT_NOM, AUT_PRENOM, AUT_DATE_NAISSANCE)
ECRIT(_ #LIV_NUM, #AUT_NUM_, ECR_ANNEE)

LIV_NUM : Entier de 0 à 65536
LIV_TITRE :  chaîne de caractères de longueur maximale 60
LIV_SS_TITRE : chaîne de caractères de longueur maximale 60
COL_NUM : Entier de 0 à 255
COL_NOM : chaîne de caractères de longueur maximale 60
COL_EDITEUR : chaîne de caractères de longueur maximale 60
AUT_NUM : Entier de 0 à 65536
AUT_NOM : chaîne de caractères de longueur maximale 60
AUT_PRENOM : chaîne de caractères de longueur maximale 60
AUT_DATE_NAISSANCE : Date
ECR_ID : Entier de 0 à 65536
ECR_ANNEE : Année

</programlisting></informalexample>
Il est à remarquer la convention de nommage des champs des relations par rapport aux noms des relations.. Pour des raisons de mise  en page, les identifiants ne sont pas soulignés, mais encacdrés de signe "_".</para>
	  <para><emphasis>Exercice :</emphasis> <emphasis role="bold">Donnez le MCD correspondant à ce modèle relationnel</emphasis></para>
	</sect3>
	<sect3>
	  <title>La requête <filename>CREATE</filename></title>
	  <para>La création d'une table en SQL se fait via la requête<command> CREATE TABLE</command>. La documentation de MySQL vous donnera la syntaxe complète de cette instruction, mais en voici un synopsys simplifié :
<informalexample>
<programlisting>CREATE TABLE nom_table (
   nom_champ1 type_champ [NULL|NOT NULL] [AUTO_INCREMENT] [PRIMARY KEY],
   nom_champ1 type_champ [NULL|NOT NULL] [AUTO_INCREMENT],
 ...
) TYPE= {BDB|HEAP|ISAM|InnoDB|MERGE|MRG_MYISAM|MYISAM};</programlisting></informalexample>
Les options <filename>NOT NULL/NULL</filename> (possibilité que la valeur d'un champ n'existe pas pour un enregistrement ou non) et <filename>AUTO_INCREMENT</filename> (incrémentation automatique d'un identifiant) sont facultatives. La clause PRIMARY KEY permet de spécifier que le champ joue le rôle de clef primaire.
</para>
<para>
Le type de la table a sont importance : en effet, selon les types choisi, la manière de stocker les informations pour MySQL sera différente (organisation de fichiers, modes d'acès, ..). Les types les plus couramment utilisés sont : 
<itemizedlist>
	      <listitem>
		<para><filename>MYISAM</filename> : moteur issu du moteur de stockage originel de MySQL (Isam), rapide, masi ne supporte pas les transactions et les clefs étrangères</para></listitem>
	      <listitem>
		<para><filename>InnoDB</filename> : réputé plus lent, supporte transactions et clefs étrangères</para></listitem></itemizedlist>
De manière générale, nous utiliserons les types de tables <filename>InnoDB</filename>.
</para>
	  <para>Nous allons maintenant créer une première table de la base de données bibliothèque, la table <filename>COLLECTION</filename> : 

<informalexample>
<programlisting>
mysql> CREATE TABLE COLLECTION (
    -> COL_NUM TINYINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -> COL_NOM VARCHAR(60) NOT NULL,
    -> COL_EDITEUR VARCHAR(60) NOT  NULL
    -> );
Query OK, 0 rows affected (0.00 sec)</programlisting></informalexample>
</para>
	  <para>Nous pouvons vérifier la bonne création de la table en utilisant les instructions SQL <command>SHOW TABLES</command> et <command>DESC</command> : 
<informalexample>
<programlisting>mysql> SHOW TABLES;
+------------------------+
| Tables_in_bibliotheque |
+------------------------+
| COLLECTION             | 
+------------------------+
1 row in set (0.00 sec)

mysql> DESC COLLECTION;
+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| COL_NUM     | tinyint(3) unsigned | NO   | PRI | NULL    | auto_increment | 
| COL_NOM     | varchar(60)         | NO   |     |         |                | 
| COL_EDITEUR | varchar(60)         | NO   |     |         |                | 
+-------------+---------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)</programlisting></informalexample>
</para>
	  <para><emphasis>Exercice :</emphasis> <emphasis role="bold">En respectant les types de données indiquée plus haut, créez la table <filename>AUTEUR</filename></emphasis></para>
	</sect3>
	<sect3>
	  <title>Index</title>
	  <para>Lors de la recherche d'informations dans une relation, MySQL parcours la table correspondante dans n'importe quel ordre. Dans le cas d'un grand nombre de lignes, cette recherche est très longue du fait du parcours de <emphasis>toute</emphasis> la table. Pour y remédier, une optimisation possible et <emphasis>fortement</emphasis> recommandée, est d'utiliser des <filename>index</filename>.</para>
<para>La création d'un index associé à un attribut ou à un ensemble ordonné d'attributs va créer une liste ordonnée des valeurs de ces attributs et de l'adresse de la ligne associée. C'est sur les valeurs de cette liste que se feront les recherches et les tris. Les algorithmes de recherche et de tri sur des ensembles ordonnées sont énormément plus rapides !</para>
<para>Ainsi, d'une recherche à coût prohibitif, on passe à une recherche sur un ensemble déjà trié. On gagne donc énormément en temps d'accès aux informations. Bien que cela ralentisse les mises à jour (insertion, suppression, modification de clé).</para>
<para>On choisira de créer des index sur les attributs qui seront les plus sollicités par les recherches ou utilisés comme critère de jointure. Par contre, on épargnera les attributs qui contiennent peu de valeurs différentes les unes des autres et ceux dont les valeurs sont très fréquemment modifiées.</para>
<para>Pour créer un index, on utilise l'instruction INDEX.   Un index peut porter sur 15 colonnes maximum. Une table peut posséder au maximum 16 indexes. Un index peut avoir une taille d'au maximum 256 octets et ne doit porter que sur des attributs NOT NULL.Un exemple de création d'index vous est proposé dans le paragraphe suivant</para>

	</sect3>
	<sect3>
	  <title>Les clefs étrangères</title>
<para>Nous avons vu toute l'importance des contraintes d'intégrité référentielle, et des clefs étrangères en particulier. Pour créer des clefs étrangères  lors de la création des tables, il faut que : 
<itemizedlist>
	      <listitem>
	      <para>les champs existent en tant que clef primaires de tables déjà créées</para></listitem>
	      <listitem>
	      <para>le type des champs clefs dans les deux tables soient les mêmes</para></listitem>
	      <listitem>
	      <para>des index soient créés sur les champs</para></listitem> </itemizedlist>
</para>
<para>	
Ainsi, la création de la table LIVRE de notre exemple se ferait avec l'instruction suivante : 
<informalexample>
<programlisting>mysql> CREATE TABLE LIVRE (
    -> LIV_NUM SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT ,
    -> LIV_TITRE VARCHAR(60) NOT NULL,
    -> LIV_SS_TITRE VARCHAR(60) NULL,
    -> COL_NUM TINYINT UNSIGNED ,
    -> PRIMARY KEY (LIV_NUM),
    -> FOREIGN KEY(COL_NUM ) REFERENCES COLLECTION(COL_NUM) ON DELETE RESTRICT  ON UPDATE CASCADE,
    -> INDEX(COL_NUM)
    -> );
Query OK, 0 rows affected (0.00 sec)</programlisting></informalexample>
</para>
<para>Dans le précédent exemple :
<itemizedlist>
	      <listitem>
		<para>le champs <filename>COL_NUM</filename> existe en tant que clef primaire  <filename>COLLECTION</filename></para></listitem>
	      <listitem>
		<para>le type de <filename>COL_NUM</filename> est bien le même dans la table <filename>COLLECTION</filename> et dans la table <filename>LIVRE</filename>.</para></listitem>
	      <listitem>
		<para>un index est créé sur <filename>COL_NUM</filename></para></listitem>
  <listitem>
		<para>on indique à MySQL de  modifier (<command>UPDATE</command>)en cascade les enregistrements de la table <filename>LIVRE</filename> si on modifie  un enregistrement lié de la table <filename>COLLECTION</filename></para></listitem>
  <listitem>
		<para>on indique à MySQL d'empêcher (<command>RESTRICT</command>) la suppression d'un enregistrement de la table <filename>COLLECTION</filename> si est lié dans la table <filename>LIVRE</filename></para></listitem> </itemizedlist>
</para>
</sect3>


      </sect2>
      <sect2>
	<title>Modification de tables</title>
	<sect3>
	  <title>La requête <filename>ALTER</filename></title>
	  <para>La création d'une relation par <command>CREATE TABLE</command> n'en rend pas définitives les spécifications. Il est possible d'en modifier la définition par la suite, à tout moment par la commande <command>ALTER TABLE</command> :
<itemizedlist>
 <listitem>
		<para> ajouter/supprimer un attribut.</para></listitem>
 <listitem>
		<para> créer/supprimer une clé primaire.</para></listitem>
 <listitem>
		<para> ajouter une contrainte d'unicité (interdire les doublons).</para></listitem>
 <listitem>
		<para> changer la valeur par défaut d'un attribut.</para></listitem>
 <listitem>
		<para> changer totalement la définition d'un attribut.</para></listitem>
 <listitem>
		<para> changer le nom de la relation.</para></listitem>
 <listitem>
		<para> ajouter/supprimer un index.</para></listitem></itemizedlist>
</para>
<para>La syntaxe générale de l'instruction est : 
<informalexample>
<programlisting>
ALTER TABLE relation MODIFY attribut definition_relative
ALTER TABLE relation ALTER attribut { SET DEFAULT valeur | DROP DEFAULT }
ALTER TABLE relation ADD UNIQUE  (attributs)
ALTER TABLE relation DROP attribut
ALTER TABLE relation ADD definition
ALTER TABLE relation ADD INDEX index (attributs)
ALTER TABLE relation DROP INDEX index
</programlisting></informalexample>
</para>
	  <para><emphasis>Exercice :</emphasis> <emphasis role="bold">Modifiez la table <filename>LIVRE</filename> de manière à faire apparître un nouveau champ : <filename>LIV_GENRE</filename>, qui soit une énumération ayant pour valeurs possible : "policier", "science-fiction", "historique"</emphasis></para>
	</sect3>
	<sect3>
	  <title>Suppression d'une table</title>
	  <para>Supprimer une table se fait par l'instruction <command>DROP TABLE nom_tabl</command>e.</para>
	</sect3>
      </sect2>
      <sect2>
	<title>Exercice d'application</title>

	<sect3>
	  <title>Création de la base de données <filename>HOTEL</filename></title>
<para>Nous avons vu dans les exercices du cours sur la modélisation le MCD d'une base de données pour la gestion d'un hôtel. Nous utiliserons cette base et son contenu dans plusieurs exercices au long de ce cours. </para>
	  <para>En utilisant l'interface de votre choix, créez en local la base de données <filename>HOTEL</filename>, qui hébergera les données de gestion de l'hôtel. </para>
	</sect3>

	<sect3>
	  <title>Création des tables et Index</title>
<para>Pour que nous partions tous sur la même base de données, je vous propose d'exécuter le script cree_hotel.sql, qui contient la définition de toutes les tables de la base de données</para>
	  <para>Téléchargez le <ulink url="cree_hotel.sql">ici</ulink> et exécutez le sur votre serveur. Donnez la liste complète des instructions qui permettent de vérifier que les tables et les colonnes ont bien été créées.</para>
	</sect3>
	<sect3>
	  <title>Modification de la structure des tables</title>
<para>On souhaite rajouter les informations suivantes : 
<itemizedlist>
	      <listitem>
	      <para>les chambres peuvent disposer ou non d'une prise ethernet pour l'accès à Internet (par défaut elles le sont toutes, sauf quelques unes)</para></listitem>
	      <listitem>
	      <para>les chambres peuvent être équipées d'un frigo (par défaut non)</para></listitem></itemizedlist>
Donnez les requêtes SQL permettant de faire ces modifications dans la structure de la base. 
</para>
	</sect3>
      </sect2>
      <sect2>
	<title>Exercice de synthèse : Gestion d'une vidéothèque personnelle</title>

    <sect3>
      <title>Présentation du sujet</title>

      <para>On vous fournit le MR suivant :</para>

      <para><filename>FILM(CodeFilm, TitreFilm, Annee, Duree, Resume,
      #CodeGenre) </filename></para>

      <para><filename>GENRE(CodeGenre, IntGenre) </filename></para>

      <para><filename>ACTEUR(CodeActeur, NomActeur, PrenomActeur)
      </filename></para>

      <para><filename>JOUER_UN_ROLE(#CodeFilm, #CodeActeur) </filename></para>

      <para><filename>SUPPORT(CodeSupport, IntSupport) </filename></para>

      <para><filename>FILM_SUPPORT(#CodeFilm,#CodeSupport) </filename></para>

      <para><filename>EMPRUNTEUR(CodePers, NomPers, PrenomPers, AdrPers,
      TelPers) </filename></para>

      <para><filename>EMPRUNT(#CodeFilm, #CodePers, DatePret,
      DateRetour)</filename></para>
    </sect3>

    <sect3>
      <title>Travail à  faire</title>

      <para>Vous devez me présenter</para>

      <orderedlist>
        <listitem>
          <para>Le modèle conceptuel des données</para>
        </listitem>

        <listitem>
          <para>Les requêtes nécéssaires à  la traduction du schéma
          relationnel sachant que :</para>

          <itemizedlist>
            <listitem>
              <para>Les types de données :</para>

              <itemizedlist>
                <listitem>
                  <para>Valeurs entières : clés primaires (sauf Date de prêt), Durée du film</para>
                </listitem>

                <listitem>
                  <para>Chaînes de caractères : Titre de film(80), Année(4),
                  Résumé( ?), Genre(20), Nom de l'acteur (25), prénom de
                  l'acteur(25), support(10), emprunteur(nom : 25, prénom : 25,
                  adresse : 50, téléphone : 14)</para>
                </listitem>

                <listitem>
                  <para>Dates : Date de prêt, date de retour</para>
                </listitem>
              </itemizedlist>
            </listitem>

            <listitem>
              <para>Les contraintes :</para>

              <itemizedlist>
                <listitem>
                  <para>Toutes les informations concernant l'emprunteur sont
                  obligatoires, ainsi que : le nom de l'acteur, le genre, le
                  support et le titre de film</para>
                </listitem>
              </itemizedlist>
            </listitem>
          </itemizedlist>
        </listitem>

        <listitem>
          <para>Ecrire les requêtes permettant: </para>

          <itemizedlist>
            <listitem>
              <para>D'indiquer que le champ « PrenomActeur » ne soit jamais
              nul.</para>
            </listitem>

            <listitem>
              <para>D'ajouter la colonne « DatNaiss » dans la table
              ACTEUR.</para>
            </listitem>

            <listitem>
              <para>De créer un index sur le champ « TitreFilm »</para>
            </listitem>
          </itemizedlist>
        </listitem>

        <listitem>
          <para>Effectuer les modifications nécessaires au stockage : </para>

          <itemizedlist>
            <listitem>
              <para>Des informations qui concernent les réalisateurs (nom,
              prénom et sexe)</para>
            </listitem>

            <listitem>
              <para>Du fait qu'un réalisateur peut réaliser de 0 à  n films</para>
            </listitem>

            <listitem>
              <para>Du fait qu'un film ait pu être réalisé par plusieurs
              réalisateurs</para>
            </listitem>

            <listitem>
              <para>Du fait que le sexe ne peut contenir que M (Masculin) ou F
              (Féminin) (M par défaut)</para>
            </listitem>

            <listitem>
              <para>Du fait qu'un film puisse appartenir à  une série de
              films</para>
            </listitem>
          </itemizedlist>
        </listitem>
   
      </orderedlist>
    </sect3>
      </sect2>
  </sect1>












<!-- ============================================================================= -->
<!-- =============================== Chapitre 2 ================================== -->
<!-- ============================================================================= -->


    <sect1>
      <title>Insertion, modification et suppression de n-uplets</title>
      <sect2>
	<title>Requêtes d'insertion</title>
	<para> Insérer une ligne dans une table MySQL revient à exécuter la commande <command>INSERT</command> :

<informalexample>
<programlisting>
INSERT INTO nom_table (Champ1, Champ2, ....)
VALUES (Valeur1, Valeur 2, ... )</programlisting></informalexample>

La liste des colonnes fournies à la suite du nom de la table correspond aux différentes colonnes que l'on souhaite remplir pour la ligne, avec les valeurs (ordonnées) qui suivent le mot clef <filename>VALUE</filename>. Dans le cas où une colonne n'est pas citée, elle prendra soit la valeur par défaut indiquée lors de la création de la table, soit la valeur NULL si elle autorisée. </para>

	<example>
	  <title>Insertion d'une collection</title>
	  <programlisting>
mysql> INSERT INTO collection(COL_NOM, COL_EDITEUR)
    -> VALUES ("Ailleurs et Demain", "Robert Laffont");
Query OK, 1 row affected (0.01 sec)
</programlisting>
Dans cet exemple, on ne donne pas la valeur de l'identifiant, qui sera automatiquement incrémenté.
	</example>

<para>Dans le cas d'insertions multiples, il est possible de les concentrer sur la même instruction, en séparant les listes de valeurs par une virgule : 

<informalexample>
<programlisting>
mysql> INSERT INTO collection(COL_NOM, COL_EDITEUR)
    -> VALUES ("Ailleurs et Demain Classiques", "Robert Laffont"),
    -> ("Antologie de la Science Fiction","J'ai Lu"),
    -> ("Special Suspens","Robert Laffont");
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> insert into auteur (AUT_NOM, AUT_PRENOM, AUT_DATE_NAISSANCE)
    -> VALUES ("Herbert", "Brian", "1944-12-10"),
    -> ("Anderson", "Kevin", "1962-03-27"),
    -> ("Herbert", "Frank", "1920-10-08");
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0
</programlisting></informalexample>


</para>

	<para>L'insertion au moyen de la requête <command>INSERT</command> renvoie une erreur si la valeur de l'identifiant est déjà prise : 
<informalexample>
<programlisting>
mysql> INSERT INTO collection(COL_NUM, COL_NOM, COL_EDITEUR)
    -> VALUES (3, "Fleuve Noir", "Livre de Poche");
ERROR 1062 (23000): Duplicate entry '3' for key 1
mysql> 
</programlisting></informalexample>

Dans ce cas il est possible d'utiliser la commande <command>REPLACE</command>  qui écrasera la ligne : 

<informalexample>
<programlisting>
mysql> REPLACE INTO collection(COL_NUM, COL_NOM, COL_EDITEUR)
    -> VALUES (3, "Fleuve Noir", "Livre de Poche");
Query OK, 2 rows affected (0.01 s)</programlisting></informalexample>

</para>
	<para>Pour visualiser le contenu la table après ces insertions, il nous faut utiliser la commande <command>SELECT</command> (nous la verrons en détail dans le chapitre suivant) : 
<informalexample>
<programlisting>mysql> select * from collection;
+---------+---------------------------------+----------------+
| COL_NUM | COL_NOM                         | COL_EDITEUR    |
+---------+---------------------------------+----------------+
|       2 | Ailleurs et Demain              | Robert Laffont | 
|       3 | Fleuve Noir                     | Livre de Poche | 
|       4 | Ailleurs et Demain Classiques   | Robert Laffont | 
|       5 | Antologie de la Science Fiction | J'ai Lu        | 
|       6 | Special Suspens                 | Robert Laffont | 
+---------+---------------------------------+----------------+
5 rows in set (0.00 sec)</programlisting></informalexample>
</para> 

	  </sect2>

      <sect2>
	<title>Requêtes de modifications</title>
	<para>Mettre des données à jour en SQL se fait généralement via les instructions <command>UPDATE</command>. Cette instruction permet la mise à jour des lignes correspondant à une (des) conditions(s). C'est pourquoi elle est généralement couplée  à la clause <filename>WHERE</filename> : 
      
<informalexample>
<programlisting>
UPDATE relation SET attribut=valeur, ... [ WHERE condition ] 
</programlisting></informalexample>

Dans le cas où la clause <filename>WHERE</filename> est absente, la mise-à-jour s'appliquera sur <emphasis>toutes</emphasis> les lignes de la table. </para>

<para>La clause WHERE, que nous verrons en détail dans le chapitre suivant, contient des expressions logiques que doivent vérifier les lignes sur lesquelles portent les modifications : 

<informalexample>
<programlisting>mysql> UPDATE collection
    -> SET COL_EDITEUR = "Le Livre de Poche"
    -> WHERE COL_NOM LIKE "Antologie de la Science Fiction";
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> UPDATE auteur set AUT_DATE_NAISSANCE = "1944-10-12"       
    -> WHERE AUT_NOM="Herbert" 
    -> AND AUT_PRENOM ="Brian";
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0</programlisting></informalexample>
</para>

<para>Il est également possible d'utiliser la valeur de la première de la colonne dans le calcul de la nouvelle valeur :

<informalexample>
<programlisting>mysql> UPDATE auteur SET AUT_NOM = UPPER(AUT_NOM);
Query OK, 3 rows affected (0.01 sec)
Rows matched: 3  Changed: 3  Warnings: 0</programlisting></informalexample>

Dans cet exemple, la mise à jour va s'appliquer à toute les lignes de la table (pas de clause <filename>WHERE</filename>). La fonction <function>UPPER</function> permet de passer une chaîne de caractère en majuscule. 

<informalexample>
<programlisting>mysql> SELECT * FROM auteur;
+---------+----------+------------+--------------------+
| AUT_NUM | AUT_NOM  | AUT_PRENOM | AUT_DATE_NAISSANCE |
+---------+----------+------------+--------------------+
|       1 | HERBERT  | Brian      | 1944-10-12         | 
|       2 | ANDERSON | Kevin      | 1962-03-27         | 
|       3 | HERBERT  | Frank      | 1920-10-08         | 
+---------+----------+------------+--------------------+
3 rows in set (0.00 sec)</programlisting></informalexample>

</para>
      </sect2>
      <sect2>
	<title>Requêtes de suppression</title>
	<para>La suppression d'un ou plusieurs enregistrement est <emphasis>définitive</emphasis>. La requête <command>DELETE</command> permet de réaliser cette tâche : 

      <informalexample>
  <programlisting> DELETE [ LOW_PRIORITY ]  FROM relation [ WHERE condition ];</programlisting></informalexample>

Comme pour l'instruction UPDATE, si la clause WHERE est omise, la table entière est détruite, et une table vide recréée. Si la clause <filename>WHERE</filename> est précisée, seules les lignes satisfaisant à la (aux) condition(s) sont supprimées. La clause <filename>LOW_PRIORITY</filename> permet de spécifier à MySQL d'attendre que les éventuels accès en lecture soient terminés pour procéder à l'effacement.

<informalexample>
	    <programlisting>mysql> DELETE FROM collection 
    -> WHERE COL_NOM = "Fleuve Noir";
Query OK, 1 row affected (0.01 sec)</programlisting></informalexample>

Si la ligne est impliqué dans une intégrité référentielle de type clef étrangère, la requête peut échouer : 

<informalexample>
<programlisting>mysql> DELETE FROM collection 
    -> WHERE COL_NOM = "Ailleurs et Demain"
    -> ;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails 
(`bibliotheque/livre`, CONSTRAINT `livre_ibfk_1` FOREIGN KEY (`COL_NUM`) REFERENCES `collection` (`COL_NUM`) ON UPDATE CASCADE)</programlisting></informalexample>

</para>

      </sect2>
      <sect2>
	<title>Bases de données exemple et exercice</title>
<para>Afin de pouvoir réaliser les exercices d'applications et les différents exemples du cours, vous pouvez télécharger les scripts de création de base et d'insertion des enregistrements : 
<itemizedlist>
	    <listitem>
	      <para>Base de données "Hotel" : <ulink url="cree_hotel.sql">création de la base</ulink> et<ulink url="insert_hotel.sql"> insertion des données</ulink></para></listitem>
	    <listitem>
	      <para>Base de données "vidéothèque" :<ulink url="videotheque.sql">création de la base</ulink> et<ulink url="bdvideo.sql"> insertion des données</ulink></para></listitem>
	    <listitem>
	      <para>Base de données "bibliothèque" : <ulink url="biblio.sql">création de la base</ulink> et<ulink url="insert_biblio.sql"> insertion des données</ulink></para></listitem>
</itemizedlist>
Vous serez, selon la configuartion des systèmes, peut-être amené à modifier l'encodage des caractères. 
</para>
      </sect2>
    </sect1>









<!-- ============================================================================= -->
<!-- =============================== Chapitre 3 ================================== -->
<!-- ============================================================================= -->


    <sect1>
      <title>Sélection de données</title>
      <sect2>
	<title>Sélection de données dans une table</title>
	<sect3>
	  <title>Syntaxe générale de la commande SELECT</title>
	<para>L'instruction SQL permettant d'extraire des données dans une table est <command>SELECT</command>. Les possibilités de conditions de sélection et de traitement des enregistrements peuvent conduire à des requêtes complexes. La syntaxe générale de la requête est : 

<informalexample>
	    <programlisting>
SELECT [ DISTINCT ] attributs
      [ FROM relation ]
      [ WHERE condition ]
      [ GROUP BY attributs [ ASC | DESC ] ]
      [ HAVING condition ]
      [ ORDER BY attributs ]
     </programlisting>
</informalexample>
Les différentes clauses sont : 
<itemizedlist>
	    <listitem><para>
attributs : la liste des colonnes à afficher en résultat (éventuellement les fonctions à exécuter sur les colonnes)</para></listitem>
	    <listitem><para><filename>DISTINCT</filename> : permet d'ignorer les doublons</para></listitem>
<listitem><para><filename>FROM</filename> : le nom de la table (ou des tables) , éventuellement aliasé dans laquelle (lesquelles) sélectionner les enregistrements</para></listitem>
<listitem><para><filename>WHERE</filename> : les conditions de sélection que doivent respecter les enregistrements</para></listitem>
<listitem><para><filename>GROUP BY</filename> : permet de grouper les résultats selon un ou plusieurs attributs</para></listitem>
<listitem><para><filename>HAVING</filename> : des critères de sélection sur des ensembles de valeurs d'un attributs après groupement</para></listitem>
	    <listitem><para><filename>ORDER BY</filename> : tri du resultats (<filename>ASC</filename> ou <filename>DESC</filename>)</para></listitem>

	
	  </itemizedlist>
</para>

	<example>
	  <title>Exemples de requêtes <command>SELECT</command></title>
	<programlisting>mysql> SELECT * FROM collection;
+---------+---------------------------------+-------------------+
| COL_NUM | COL_NOM                         | COL_EDITEUR       |
+---------+---------------------------------+-------------------+
|       2 | Ailleurs et Demain              | Robert Laffont    | 
|       4 | Ailleurs et Demain Classiques   | Robert Laffont    | 
|       5 | Antologie de la Science Fiction | Le Livre de Poche | 
|       6 | Special Suspens                 | Robert Laffont    | 
+---------+---------------------------------+-------------------+
4 rows in set (0.00 sec)

mysql> SELECT COL_EDITEUR FROM collection;
+-------------------+
| COL_EDITEUR       |
+-------------------+
| Robert Laffont    | 
| Robert Laffont    | 
| Le Livre de Poche | 
| Robert Laffont    | 
+-------------------+
4 rows in set (0.00 sec)

mysql> SELECT DISTINCT COL_EDITEUR FROM collection;
+-------------------+
| COL_EDITEUR       |
+-------------------+
| Robert Laffont    | 
| Le Livre de Poche | 
+-------------------+
2 rows in set (0.35 sec)

mysql> SELECT DISTINCT COL_EDITEUR FROM collection ORDER BY COL_EDITEUR ASC;
+-------------------+
| COL_EDITEUR       |
+-------------------+
| Le Livre de Poche | 
| Robert Laffont    | 
+-------------------+
2 rows in set (0.00 sec)</programlisting></example>

<para>Les prédicats de sélection de la clause WHERE sont une série de tests logiques sur la valeur des colonnes de chaque enregistrement. La requête parcourt tous les enregistrements de la table, ne renvoyant que les lignes qui satisfont aux conditions. Nous examinons ces conditions dans les paragraphes suivant.</para>
</sect3>
	<sect3>
	  <title>Opérateurs et fonctions </title>
<para>Il est possible d'intégrer des calculs ou des opérations logiques dans les prédicats de sélection et dans l'affichage des résultats. Les tableaux suivants résument ces opérateurs et fonctions : 

    <table>
          <title>Opérateurs</title>

          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Opérateur</emphasis></entry>

                <entry><emphasis role="bold">Symbole</emphasis></entry>
              </row>
         <row>
                <entry>Arithmétiques </entry>

                <entry>+, -, *, /, %</entry>
              </row>

              <row>
                <entry>Relationnels</entry>

                <entry>&lt;, &lt;=, =, &gt;, &gt;=, &lt;&gt;, LIKE</entry>
              </row>

              <row>
                <entry>Logiques</entry>

		    <entry><filename>AND, OR, NOT, BETWEEN, IN</filename>
</entry>
              </row>
            
            </tbody>
          </tgroup>
        </table>
<informalexample>
	      <programlisting>mysql> SELECT * FROM auteur 
    -> WHERE AUT_NOM = "HERBERT";
+---------+---------+------------+--------------------+
| AUT_NUM | AUT_NOM | AUT_PRENOM | AUT_DATE_NAISSANCE |
+---------+---------+------------+--------------------+
|       1 | HERBERT | Brian      | 1944-10-12         | 
|       3 | HERBERT | Frank      | 1920-10-08         | 
+---------+---------+------------+--------------------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM collection 
    -> WHERE COL_NOM LIKE "AIllEUrs%";
+---------+-------------------------------+----------------+
| COL_NUM | COL_NOM                       | COL_EDITEUR    |
+---------+-------------------------------+----------------+
|       2 | Ailleurs et Demain            | Robert Laffont | 
|       4 | Ailleurs et Demain Classiques | Robert Laffont | 
+---------+-------------------------------+----------------+
2 rows in set (0.00 sec)
</programlisting></informalexample>


   
 <table>
          <title>Fonctions sur les chaînes de caractères</title>


          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Fonction</emphasis></entry>

                <entry><emphasis role="bold">Description</emphasis></entry>
              </row>
         <row>
		    <entry> <filename>TRIM(x)</filename></entry>
                <entry>Supprime les espaces de début et de fin de chaîne</entry>
              </row>

              <row>
                <entry><filename>LOWER(x)</filename></entry>
                <entry>Converti en minuscules</entry>
              </row>

              <row>
                <entry><filename>UPPER(x)</filename></entry>
                <entry>Converti en majuscules</entry>
              </row>
             <row>
                <entry><filename>LOCATE(x,y)</filename></entry>
                <entry>Renvoie la position de lpremière occurence de x dans y, 0 si pas trouvé</entry>
              </row>
 <row>
                <entry><filename>CONCAT(x,y,...) </filename></entry>
                <entry>Concatène les arguments</entry>
              </row>
 <row>
                <entry><filename>SUBSTRING(s,i,n) </filename></entry>
                <entry>Retourne les n derniers caractères de s à partir de i</entry>
              </row>
 <row>
                <entry><filename>SOUNDEX(x) </filename></entry>
                <entry>Retourne une présentation phonétique de x</entry>
              </row>
 <row>
                <entry><filename>LENGTH(x)</filename></entry>
                <entry>Renvoi la longueur de x</entry>
              </row>
            </tbody>
          </tgroup>
        </table> 

<informalexample>
	      <programlisting>mysql>  SELECT AUT_NOM, AUT_PRENOM, UPPER(CONCAT(SUBSTRING(AUT_NOM,1,1) , SUBSTRING(AUT_PRENOM,1 ,1))) AS INITIALES
    -> FROM auteur;
+----------+------------+-----------+
| AUT_NOM  | AUT_PRENOM | INITIALES |
+----------+------------+-----------+
| HERBERT  | Brian      | HB        | 
| ANDERSON | Kevin      | AK        | 
| HERBERT  | Frank      | HF        | 
+----------+------------+-----------+
3 rows in set (0.00 sec)</programlisting></informalexample>


<table>
          <title>Fonctions sur les dates et heures</title>

          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Fonction</emphasis></entry>

                <entry><emphasis role="bold">Description</emphasis></entry>
              </row>
         <row>
		    <entry> <filename>NOW()</filename></entry>
                <entry>Retourne la date et heure du jour</entry>
              </row>

              <row>
                <entry><filename>TO_DAYS(x)</filename></entry>
                <entry>Conversion de la date X en nombre de jours depuis la date 0
</entry>
              </row>

              <row>
                <entry><filename>DAYOFWEEK(x)</filename></entry>
                <entry>Retourne un entier indiquant la position de la date dans la semaine</entry>
              </row>
          <row>
                <entry><filename>DAYOFMONTH(x)</filename></entry>
                <entry>Retourne un entier indiquant la position de la date dans le mois</entry>
              </row>
     <row>
                <entry><filename>DAYOFYEAR(x)</filename></entry>
                <entry>Retourne un entier indiquant la position de la date dans l'année</entry>
              </row>
     <row>
                <entry><filename>SECOND(x), MINUTE(x),HOUR(x), MONTH(x),YEAR(x), WEEK(x)
</filename></entry>
                <entry>Retournent respectivement les secondes, minutes, heures, mois, anné et semaine de la ate.
</entry>
              </row>       
            </tbody>
          </tgroup>
        </table> 
<informalexample>
	      <programlisting>mysql> SELECT * FROM auteur 
    -> WHERE YEAR(AUT_DATE_NAISSANCE) > 1940;
+---------+----------+------------+--------------------+
| AUT_NUM | AUT_NOM  | AUT_PRENOM | AUT_DATE_NAISSANCE |
+---------+----------+------------+--------------------+
|       1 | HERBERT  | Brian      | 1944-10-12         | 
|       2 | ANDERSON | Kevin      | 1962-03-27         | 
+---------+----------+------------+--------------------+
2 rows in set (0.00 sec)</programlisting></informalexample>

<table>
          <title>Fonctions d'agrégation</title>

          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Fonction</emphasis></entry>

                <entry><emphasis role="bold">Symbole</emphasis></entry>
              </row>
         <row>
		    <entry> <filename>COUNT([DISTINCT]x,y,...)</filename></entry>
                <entry>Compte le nombre de lignes renvoyées par une requêtes</entry>
              </row>

              <row>
		    <entry><filename>MIN(x), MAX(x), AVG(x), SUM(x) </filename></entry>
                <entry>Calculent respectivement le minimum, le maximum, la moyenne et la somme des valeurs de l'attribut X
</entry>
              </row>

            </tbody>
          </tgroup>
        </table>

</para>

	  <informalexample>
	    <programlisting>mysql> SELECT COUNT(*) as NB_AUTEURS FROM auteur;
+------------+
| NB_AUTEURS |
+------------+
|          3 | 
+------------+
1 row in set (0.00 sec)

</programlisting>
</informalexample>

 <table>
          <title>Fonctions mathématiques</title>

          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Fonction</emphasis></entry>

                <entry><emphasis role="bold">Description</emphasis></entry>
              </row>
         <row>
		    <entry><function>ABS(x)</function>  </entry>
                <entry>Valeur absolue de X</entry>
              </row>

              <row>
                <entry><function>SIGN(x) </function> </entry>
                <entry> Signe de X, retourne -1, 0 ou 1</entry>
              </row>

              <row>
                <entry><function>FLOOR(x) </function> </entry>
                <entry>Arrondi à l'entier inférieur</entry>
              </row>
            
 <row>
                <entry><function>CEILING(x) </function> </entry>
                <entry>Arrondi à l'entier supérieur</entry>
              </row>
 <row>
                <entry> <function>ROUND(x)</function> </entry>
                <entry>Arrondi à l'entier le plus proche</entry>
              </row>
 <row>
                <entry> <function>EXP(x), LOG(x), SIN(x),COS(x), TAN(x), PI()</function> </entry>
                <entry>Exponentiel, logarithme, sinus ...</entry>
              </row>
 <row>
                <entry><function>POW(x,y) </function> </entry>
                <entry>Retourne X à la puissance Y</entry>
              </row>
 <row>
                <entry> <function>RAND(x)  </function> </entry>
                <entry> Retourne un nombre aléatoire entre 0 et X</entry>
              </row>
 <row>
                <entry><function>TRUNCATE(x,y) </function> </entry>
                <entry>Tronque le nombre X à la Yème décimale</entry>
              </row>

            </tbody>
          </tgroup>
        </table>


	</sect3>
	  </sect2>

 
     <sect2>
	<title>Groupages et sous-ensembles</title>
	<para>SQL dispose de <emphasis>fonctions d'agrégats</emphasis> vues dans la paragraphe précédents  : <command>MAX</command>, <command>MIN</command>, <command>SUM</command>, <command>AVG</command>, <command>COUNT</command>. Elles permettent d'effectuer des calculs statistiques sur des lignes résultant de requêtes de sélection, et sont souvent employées avec la clause <filename>GROUP BY</filename>, permettant d'opérer ces calculs sur des sous-ensembles de lignes. </para>
	<para>De manière générale on peut dire que le <filename>GROUP BY</filename> doit porter sur l'ensemble des attributs listés hors des fonctions d'agrégats : 

<informalexample>
	    <programlisting>mysql> SELECT COUNT(COL_NOM), COL_EDITEUR                 
    -> FROM collection 
    -> GROUP BY COL_EDITEUR;
+----------------+-------------------+
| COUNT(COL_NOM) | COL_EDITEUR       |
+----------------+-------------------+
|              1 | Le Livre de Poche | 
|              3 | Robert Laffont    | 
+----------------+-------------------+
2 rows in set (0.00 sec)

mysql> select count(LIV_NUM) as NB_LIV, COL_NOM, COL_EDITEUR     
    ->  FROM livre
    -> JOIN collection
    -> ON collection.COL_NUM = livre.COL_NUM
    -> GROUP BY COL_NOM, COL_EDITEUR;
+--------+-------------------------------+----------------+
| NB_LIV | COL_NOM                       | COL_EDITEUR    |
+--------+-------------------------------+----------------+
|      8 | Ailleurs et Demain            | Robert Laffont | 
|      2 | Ailleurs et Demain Classiques | Robert Laffont | 
+--------+-------------------------------+----------------+
2 rows in set (0.00 sec)


mysql> select count(LIV_NUM) as NB_LIV, COL_NOM, COL_EDITEUR   
  ->  FROM collection    -> LEFT JOIN livre       
  -> ON collection.COL_NUM = livre.COL_NUM   
  -> GROUP BY COL_NOM, COL_EDITEUR
;+--------+---------------------------------+-------------------+
| NB_LIV | COL_NOM                         | COL_EDITEUR       |
+--------+---------------------------------+-------------------+
|      8 | Ailleurs et Demain              | Robert Laffont    | 
|      2 | Ailleurs et Demain Classiques   | Robert Laffont    | 
|      0 | Antologie de la Science Fiction | Le Livre de Poche | 
|      0 | Special Suspens                 | Robert Laffont    | 
+--------+---------------------------------+-------------------+
4 rows in set (0.00 sec)</programlisting></informalexample>


</para>
	<para>Le groupage des résultats se faisant <emphasis>après</emphasis> la sélection des données, il n'est pas possible de fixer des contraintes sur les fonctions d'agrégats dans la clause WHERE. HAVING permet ainsi d'éffectuer des sélections sur les calculs statistiques : 

<informalexample>
	    <programlisting>mysql> SELECT AUT_NOM, AUT_PRENOM, COUNT(ECR_NUM_LIVRE) AS NB_LIVRES      
   -> FROM auteur, ecrit    
   -> WHERE auteur.AUT_NUM = ecrit.ECR_NUM_AUTEUR   
   -> GROUP BY AUT_NOM, AUT_PRENOM;
+----------+------------+-----------+
| AUT_NOM  | AUT_PRENOM | NB_LIVRES |
+----------+------------+-----------+
| ANDERSON | Kevin      |         1 | 
| HERBERT  | Brian      |         4 | 
| HERBERT  | Frank      |         2 | 
+----------+------------+-----------+
3 rows in set (0.00 sec)

mysql> SELECT AUT_NOM, AUT_PRENOM, COUNT(ECR_NUM_LIVRE) AS NB_LIVRES
      -> FROM auteur, ecrit
    -> WHERE auteur.AUT_NUM = ecrit.ECR_NUM_AUTEUR
    -> AND NB_LIVRES > 2
    -> GROUP BY AUT_NOM, AUT_PRENOM;
ERROR 1054 (42S22): Unknown column 'NB_LIVRES' in 'where clause'

mysql> SELECT AUT_NOM, AUT_PRENOM, COUNT(ECR_NUM_LIVRE) AS NB_LIVRES
      -> FROM auteur, ecrit
    -> WHERE auteur.AUT_NUM = ecrit.ECR_NUM_AUTEUR
    -> GROUP BY AUT_NOM, AUT_PRENOM
     -> HAVING NB_LIVRES > 2;
+---------+------------+-----------+
| AUT_NOM | AUT_PRENOM | NB_LIVRES |
+---------+------------+-----------+
| HERBERT | Brian      |         4 | 
+---------+------------+-----------+
1 row in set (0.00 sec)</programlisting>
</informalexample>

<remark>Exercice : Lister les livres qui ont été co-écrits.</remark>

</para>
<para>Il n'est pas possible de combiner les fonctions d'agrégats entre elles : 

<informalexample>
	    <programlisting>mysql> SELECT AUT_NOM, AUT_PRENOM, MAX(COUNT(ECR_NUM_LIVRE)) AS NB_LIVRES
      -> FROM auteur, ecrit
    -> WHERE auteur.AUT_NUM = ecrit.ECR_NUM_AUTEUR
    -> GROUP BY AUT_NOM, AUT_PRENOM;
ERROR 1111 (HY000): Invalid use of group function</programlisting></informalexample>

Connaître l'auteur dont on a le plus d'ouvrages dans la base nécessite l'emploi de sous-requêtes, que nous allons voir dans le prochain chapitre. 

</para>
      </sect2>
   
      <sect2>
	<title>Jointures et sous-requêtes</title>
	<sect3>
	  <title>Jointures</title>
<para>Les jointures sont l'opération algébrique qui consiste à recouper les enregistrements de plusieurs tables de manière à retrouver les dépendances exprimées dès le modèle conceptuel des données. Une première méthode pour les jointures consistent à indiquer dans les prédicats de sélection l'égalité d'attributs (généralement la clef primaire et la clef étrangère) : 

<informalexample>
<programlisting>
mysql>  SELECT COL_NOM, LIV_TITRE, LIV_SS_TITRE FROM livre, collection
    ->  WHERE livre.COL_NUM = collection.COL_NUM;
+-------------------------------+------------------------+-------------------------+
| COL_NOM                       | LIV_TITRE              | LIV_SS_TITRE            |
+-------------------------------+------------------------+-------------------------+
| Ailleurs et Demain            | Dune                   |                         | 
| Ailleurs et Demain            | Dune                   | Le Messie de Dune       | 
| Ailleurs et Demain            | Dune                   | L'empereur Dieu de Dune | 
| Ailleurs et Demain            | Dune                   | Les hérétiques de Dune  | 
| Ailleurs et Demain            | Dune                   | Les enfants de Dune     | 
| Ailleurs et Demain            | Dune, la génèse        | La Guerre des machines  | 
| Ailleurs et Demain            | Dune, la génèse        | Le Jihad Butlérien      | 
| Ailleurs et Demain            | Dune, la génèse        | La Bataille de Corrin   | 
| Ailleurs et Demain Classiques | L'Effet Lazare         |                         | 
| Ailleurs et Demain Classiques | L'Homme de deux mondes |                         | 
+-------------------------------+------------------------+-------------------------+
10 rows in set (0.00 sec)

</programlisting></informalexample>

Les attributs portant le même nom dans les deux tables, nous sommes obligés de les préfixer du nom de chaque table.
</para>
	  <para>Les requêtes utilisant souvent des jointures, MySQL implémente ces opérations de manière optimisé en utilisant la clause <filename>JOIN</filename> :


<informalexample>
	      <programlisting>SELECT attributs FROM table1 
INNER JOIN table1
ON table1.att1 = table2.att2
</programlisting></informalexample>
<informalexample>
	      <programlisting> 
mysql> SELECT * FROM collection;
+---------+---------------------------------+-------------------+
| COL_NUM | COL_NOM                         | COL_EDITEUR       |
+---------+---------------------------------+-------------------+
|       2 | Ailleurs et Demain              | Robert Laffont    | 
|       4 | Ailleurs et Demain Classiques   | Robert Laffont    | 
|       5 | Antologie de la Science Fiction | Le Livre de Poche | 
|       6 | Special Suspens                 | Robert Laffont    | 
+---------+---------------------------------+-------------------+
4 rows in set (0.00 sec)

mysql> SELECT COL_NOM, LIV_TITRE, LIV_SS_TITRE FROM collection
    -> INNER JOIN livre 
    -> ON collection.COL_NUM = livre.COL_NUM; 
+-------------------------------+------------------------+-------------------------+
| COL_NOM                       | LIV_TITRE              | LIV_SS_TITRE            |
+-------------------------------+------------------------+-------------------------+
| Ailleurs et Demain            | Dune                   |                         | 
| Ailleurs et Demain            | Dune                   | Le Messie de Dune       | 
| Ailleurs et Demain            | Dune                   | L'empereur Dieu de Dune | 
| Ailleurs et Demain            | Dune                   | Les hérétiques de Dune  | 
| Ailleurs et Demain            | Dune                   | Les enfants de Dune     | 
| Ailleurs et Demain            | Dune, la génèse        | La Guerre des machines  | 
| Ailleurs et Demain            | Dune, la génèse        | Le Jihad Butlérien      | 
| Ailleurs et Demain            | Dune, la génèse        | La Bataille de Corrin   | 
| Ailleurs et Demain Classiques | L'Effet Lazare         |                         | 
| Ailleurs et Demain Classiques | L'Homme de deux mondes |                         | 
+-------------------------------+------------------------+-------------------------+
10 rows in set (0.00 sec)

</programlisting></informalexample>

Dans le cas de <filename>INNER JOIN</filename>, seules les lignes de la première table étant liées avec des enregistrements de la deuxième table apparaissent. Dans l'exemple, certaines collections ne sont donc pas listées. Cette jointure est appelée jointure <emphasis>interne</emphasis>, et est  la plus utilisée. Le mot clef <filename>INNER</filename> est ainsi facultatif. 
</para>
	  <para>Les clauses <filename>LEFT JOIN</filename> (respectivement <filename>RIGHT JOIN</filename>) permettent de lister tous les enregistrements de la première table (respectivement la deuxième), même si ils n'ont pas de correspondance dans l'autre table. Dans ce cas, l'attribut non renseigné est noté <filename>NULL</filename>.

<informalexample>
	      <programlisting>mysql> SELECT COL_NOM, LIV_TITRE, LIV_SS_TITRE FROM collection
    -> LEFT JOIN livre 
    -> ON collection.COL_NUM = livre.COL_NUM; 
+---------------------------------+------------------------+-------------------------+
| COL_NOM                         | LIV_TITRE              | LIV_SS_TITRE            |
+---------------------------------+------------------------+-------------------------+
| Ailleurs et Demain              | Dune                   |                         | 
| Ailleurs et Demain              | Dune                   | Le Messie de Dune       | 
| Ailleurs et Demain              | Dune                   | L'empereur Dieu de Dune | 
| Ailleurs et Demain              | Dune                   | Les hérétiques de Dune  | 
| Ailleurs et Demain              | Dune                   | Les enfants de Dune     | 
| Ailleurs et Demain              | Dune, la génése        | La Guerre des machines  | 
| Ailleurs et Demain              | Dune, la génése        | Le Jihad Butl~rien      | 
| Ailleurs et Demain              | Dune, la génése        | La Bataille de Corrin   | 
| Ailleurs et Demain Classiques   | L'Effet Lazare         |                         | 
| Ailleurs et Demain Classiques   | L'Homme de deux mondes |                         | 
| Antologie de la Science Fiction | NULL                   | NULL                    | 
| Special Suspens                 | NULL                   | NULL                    | 
+---------------------------------+------------------------+-------------------------+
12 rows in set (0.00 sec)</programlisting></informalexample>

<informalexample>
	      <programlisting>mysql> SELECT * FROM auteur;
+---------+------------+------------+--------------------+
| AUT_NUM | AUT_NOM    | AUT_PRENOM | AUT_DATE_NAISSANCE |
+---------+------------+------------+--------------------+
|       1 | HERBERT    | Brian      | 1944-10-12         | 
|       2 | ANDERSON   | Kevin      | 1962-03-27         | 
|       3 | HERBERT    | Frank      | 1920-10-08         | 
|       4 | Stephenson | Neil       | 1957-09-05         | 
+---------+------------+------------+--------------------+
4 rows in set (0.00 sec)



mysql> select AUT_NOM, AUT_PRENOM, LIV_TITRE  ,LIV_SS_TITRE FROM livre
    -> RIGHT JOIN ecrit
    -> ON ecrit.ECR_NUM_LIVRE=livre.LIV_NUM
    -> RIGHT JOIN auteur 
    -> ON ecrit.ECR_NUM_AUTEUR = auteur.AUT_NUM ;
+------------+------------+------------------------+------------------------+
| AUT_NOM    | AUT_PRENOM | LIV_TITRE              | LIV_SS_TITRE           |
+------------+------------+------------------------+------------------------+
| HERBERT    | Brian      | Dune, la génése        | La Guerre des machines | 
| HERBERT    | Brian      | Dune, la génése        | Le Jihad Butlérien     | 
| HERBERT    | Brian      | L'Homme de deux mondes |                        | 
| ANDERSON   | Kevin      | NULL                   | NULL                   | 
| HERBERT    | Frank      | L'Effet Lazare         |                        | 
| HERBERT    | Frank      | L'Homme de deux mondes |                        | 
| Stephenson | Neil       | NULL                   | NULL                   | 
+------------+------------+------------------------+------------------------+
7 rows in set (0.00 sec)</programlisting></informalexample>

Pour des raisons de lisibilité des requêtes, il est préférable de n'utiliser que la syntaxe LEFT. Cette jointure est appelée jointure <emphasis>externe</emphasis>.

</para>
<para>Les jointures externes sont également utilisées pour permettre de détecter les lignes d'une table n'ayant pas de correspondance dans une autre :  

<informalexample>
	      <programlisting>mysql> SELECT AUT_NOM, AUT_PRENOM FROM auteur
    -> LEFT JOIN ecrit 
    -> ON ecrit.ECR_NUM_AUTEUR = auteur.AUT_NUM
    -> WHERE ecrit.ECR_NUM_LIVRE IS NULL;
+------------+------------+
| AUT_NOM    | AUT_PRENOM |
+------------+------------+
| ANDERSON   | Kevin      | 
| Stephenson | Neil       | 
+------------+------------+
2 rows in set (0.00 sec)</programlisting></informalexample>

On utilise alors le fait qu'une colonne n'ayant pas de correspondance est affichée à <filename>NULL</filename>. 

</para>
	</sect3>

	<sect3>
	  <title>Sous-requêtes et vues anonymes</title>
	  <para>Les sous-requêtes sont supportées depuis la version 4.1 de MySQL. Elles sont souvent nécessaires pour l'extraction de certaines informations depuis la base de données, mais ne devraient pas être systématiquement employées en lieu et place des jointures. Une sous-requête est une commande <command>SELECT</command> imbriquée dans une autre commande, une vue anonyme est une sous-requête placée dans une clause <filename>FROM</filename>. </para>

	  <para>Un des emplois classiques des sous-requêtes est d'obtenir une valeur placée dans une clause de sélection ou de restrictions d'une requête <command>SELECT</command>. Ainsi rechercher l'auteur le plus jeune de la base reviendrait à : 

<informalexample>
	      <programlisting>
mysql> SELECT AUT_NOM, AUT_PRENOM FROM auteur
    -> WHERE TO_DAYS(AUT_DATE_NAISSANCE) =
    ->      (SELECT MAX(TO_DAYS(AUT_DATE_NAISSANCE)) 
   ->       FROM auteur as TABLE_TMP);
+----------+------------+
| AUT_NOM  | AUT_PRENOM |
+----------+------------+
| ANDERSON | Kevin      | 
+----------+------------+
1 row in set (0.00 sec)</programlisting></informalexample>

La sous requête est effectuée en premier, renvoyant <emphasis>une seule valeur</emphasis>, qui est intégré à la requête principale avant son exécution. 
</para>

<para>------------ TO DO : IN ---------------</para>


	  <para>Dans le cas où la sous-requête renvoie plusieurs lignes, il est possible de tester la valeur d'un attribut de la requête (dans les filtres <filename>WHERE</filename> et <filename>HAVING</filename>) en employant les mots clef : 
<itemizedlist>
	      <listitem>
		<para><filename>ALL</filename> : la condition sera vérifié si la comparaison entre l'attribut et le résultat de la sous requête est vérifiée pour <emphasis>toutes les lignes</emphasis> de la sous-requête</para> </listitem>
	      <listitem>
		<para><filename>ANY</filename> : la condition sera vérifié si la comparaison entre l'attribut et le résultat de la sous requête est vérifiée pour<emphasis> au moins une ligne</emphasis> de la sous-requête </para></listitem></itemizedlist>

</para>

<informalexample>
	  <programlisting>mysql> SELECT AUT_NOM, AUT_PRENOM FROM auteur
    -> WHERE YEAR(AUT_DATE_NAISSANCE) >= ALL
    ->      (SELECT AUT_DATE_NAISSANCE FROM auteur as TABLE_TMP);
+----------+------------+
| AUT_NOM  | AUT_PRENOM |
+----------+------------+
| ANDERSON | Kevin      | 
+----------+------------+
1 row in set (0.00 sec)</programlisting></informalexample>



	  <para>Les <emphasis>vues anonymes</emphasis> permettent de faire des sélections dans des tables temporaires qui sont utilisées dans la clause <filename>FROM</filename>. Ces tables doivent être nommées, MySQL devant les stocker en mémoire afin d'exécuter la requête principal. Ainsi, touver l'auteur dont on a le plus de livres peut se faire par la requête : 

<informalexample>
	      <programlisting>mysql> SELECT AUT_NOM, AUT_PRENOM, COUNT(ECR_NUM_LIVRE) AS NB_LIVRES 
    -> FROM auteur, ecrit 
    -> WHERE auteur.AUT_NUM=ecrit.ECR_NUM_AUTEUR
    -> GROUP BY AUT_NOM, AUT_PRENOM 
    -> HAVING NB_LIVRES =   
    ->     (SELECT MAX(NB) FROM (
    ->         SELECT COUNT(ECR_NUM_LIVRE) AS NB, ECR_NUM_AUTEUR
    ->         FROM ecrit
    ->         GROUP BY ECR_NUM_AUTEUR 
    ->         ) AS TABLE_TMP
    -> );
+---------+------------+-----------+
| AUT_NOM | AUT_PRENOM | NB_LIVRES |
+---------+------------+-----------+
| HERBERT | Brian      |         4 | 
+---------+------------+-----------+
1 row in set (0.00 sec)</programlisting></informalexample>

</para>

	</sect3>

      </sect2>

    <sect2>
      <title>Exercices</title>
<sect3>
	<title>Exercice d'application : gestion d'un hôtel</title>
<para>
Vous devez concevoir les requêtes permettant d'obtenir les informations suivantes (vous êtes libres d'afficher les colonnes de votre choix, tant que l'énoncé est respecté) :

<orderedlist>
<!-- ===================== Requêtes sur une seule table ===================== --> 
	      <listitem>
		<para>La liste des chambres de l'hôtel</para>
	      </listitem>
  <listitem>
		<para>La liste des chambres de l'hôtel ne possédant pas de bains</para>
	      </listitem>
  <listitem>
		<para>La liste des chambres de l'hôtel ne possédant pas de bain, et n'étant pas au RDC</para>
	      </listitem>
  <listitem>
		<para>La liste des chambres de l'hôtel proposant au moins 4 places de couchage</para>
	      </listitem>
  <listitem>
		<para>Le nombre de chambre par étage</para>
	      </listitem>
  <listitem>
		<para>Le nombre de chambre proposant au moins 4 places de couchage par étage</para>
	      </listitem>
  <listitem>
		<para>Le nombre de clientes enregistrées</para>
	      </listitem>
  <listitem>
		<para>La liste des mails sur le domaine Wanadoo</para>
	      </listitem>
  <listitem>
		<para>Modifier les adresses de manière à ajouter l'arondissement quand elles sont situées à Paris. Par exemple<filename> 75116 PARIS</filename> deviendra <filename>75116 PARIS 16</filename>.</para>
	      </listitem>
  <listitem>
		<para>La liste des clients "professionnels"</para>
	      </listitem>
  <listitem>
		<para>La moyenne du prix d'une nuit sur tous les tarifs enregistrés</para>
	      </listitem>
  <listitem>
		<para>Le chiffre d'affaire maximum si l'hôtel avait été loué au complet tous les jours de l'année 2006</para>
	      </listitem>
 </orderedlist>
<!-- ===================== Requêtes jointures ===================== - --> 
 
<orderedlist>
 <listitem>
		<para>La liste des clients avec leur titre en toute lettre</para>
	      </listitem>
  <listitem>
		<para>La liste des factures associées aux clients avec leur titre en toute lettre</para>
	      </listitem>
<listitem>
		<para>La liste des factures associées aux clients avec leur titre en toute lettre</para>
	      </listitem>
<listitem>
		<para>La liste des emails en base avec les clients qui leur correspondent</para>
	      </listitem>

<listitem>
		<para>La liste de tous les clients avec éventuellement leurs mails</para>
	      </listitem>
<listitem>
		<para>La liste des clients dont on n'a pas de mails</para>
	      </listitem>

 </orderedlist>

 <!-- ===================== sous - requêtes===================== --> 
<orderedlist>
<listitem>
		<para>Le nombre moyen de chambres par étage</para>
	      </listitem>
<listitem>
		<para>La liste de toutes les factures et de leur montant total</para>
	      </listitem>
<listitem>
		<para>Les détails de la facture la plus chère</para>
	      </listitem>
	    </orderedlist>

</para>

	  </sect3>
      
<sect3>
	<title>Exercice de synthèse : vidéothèque</title>
<para>
Vous devez concevoir les requêtes permettant d'obtenir les informations suivantes (vous êtes libres d'afficher les colonnes de votre choix, tant que l'énoncé est respecté) :

<orderedlist>

<listitem>
		<para>Liste des films et de leurs acteurs</para>
	      </listitem>
<listitem>
		<para>Liste des films par genre</para>
	      </listitem>
<listitem>
		<para>Liste de tous les films commençant par "S", de leur genre, de leur type de support et de leurs acteurs</para>
	      </listitem>
<listitem>
		<para>Liste des films disponibles dans au moins deux types de support</para>
	      </listitem>
<listitem>
		<para>Somme des durées des films par genre</para>
	      </listitem>
<listitem>
		<para>Genre contenant le maximum de films </para>
	      </listitem>
<listitem>
		<para>Liste des acteurs et des réalisateurs</para>
	      </listitem>
<listitem>
		<para>Durée moyenne d'un film de la vidéothèque</para>
	      </listitem>
<listitem>
		<para>Titre de film ayant le plus grand nombre de caractères</para>
	      </listitem>
<listitem>
		<para>Liste des films actuellement absents de la vidéothèque</para>
	      </listitem>
	    </orderedlist>

</para>

	  </sect3>

    </sect2>


    </sect1>








<!-- ============================================================================= -->
<!-- =============================== Chapitre 4 ================================== -->
<!-- ============================================================================= -->


    <sect1>
      <title>Administration, sécurisation  et optimisation de MySQL</title>
<sect2>
	<title>Transactions</title>
	  </sect2>
      
<sect2>
	<title>Administration de MySQL</title>
	  </sect2>

      <sect2>
	<title>Sécurisation et gestion des utilisateurs</title>

      </sect2>
      <sect2>
	<title>Optimisation de MySQL</title>


      </sect2>
    </sect1>


 <!-- =================================================================== -->
  <bibliography> 
    <title>Références</title> 

    <bibliodiv> 
      <biblioentry> 
 	<abbrev>1</abbrev> 
	<authorgroup>
	  <author>	   
	    <personname>
	    <firstname>Suehring</firstname>
	    <surname>Steve</surname>
	  </personname>	 
 </author> <author>
	    <personname>
	    <firstname>Thomson</firstname>
	    <surname>Laura</surname>
	  </personname>	  
 	</author>
	</authorgroup>
 	<title>MySQL Bible</title> 
 	<edition> 
Wiley Publishing
         </edition> 
      </biblioentry> 

      <biblioentry> 
 	<abbrev>2</abbrev> 
	<authorgroup>
	  <author>	   
	    <personname>Jay Greenspan and Brad Bulger
	    <firstname>Greenspan</firstname>
	    <surname>Jay</surname>
	  </personname>	 
   </author> <author>
	    <personname>
	    <firstname>Bulger</firstname>
	    <surname>Brad</surname>
	  </personname>	  
 	</author>
	</authorgroup>
 	<title>MySQL/PHP Database Applications </title> 
 	<edition> 
M&amp;T Books
         </edition> 
      </biblioentry> 

      <biblioentry> 
 	<abbrev>3</abbrev> 

 	<title>www.nexen.net</title> 
 	<edition> 

         </edition> 
  </biblioentry> 
      <biblioentry> 
 	<abbrev>4</abbrev> 

 	<title>www.mysql.com</title> 
 	<edition> 
MySQL AB
         </edition> 
      </biblioentry>     
    
    </bibliodiv> 
  </bibliography> 
</article>