# Les bases de données/SQL : Data Description Language

Nous avons vu beaucoup de théorie dans les chapitres précédents. Il est temps de passer à un peu plus de pratique : nous allons aborder les bases du langage SQL. Pour faire simple, ce langage permet de gérer des bases de données. Ce langage permet d'envoyer des ordres à un SGBD, ces ordres permettant de créer des tables, d'y insérer des données, de les consulter, ou de les supprimer. On divise ce langage SQL en deux sous-langages :
* le SQL Data Description Language, qui permet de créer des tables et de définir comment seront stockées les données dans la base de données ;
* et le SQL Data Manipulation Language, qui permet de créer, consulter et supprimer le contenu des tables.

## Tables, enregistrements et attributs

Créer une table est possible en utilisant l'expression suivante :
Pour définir les colonnes des tables, il suffit de préciser quels sont leurs noms et leur type.

### Nom

Le nom des attributs d'une table est soumis à quelques règles relativement strictes. Premièrement, il ne peut contenir que des lettres, des chiffres, et les signes $, _, et # : pas d'espaces, pas d'accents, etc. De plus, un nom d'attribut ne peut pas commencer par un chiffre. De plus, il faut signaler que les lettres peuvent être écrites en majuscules et minuscules : cela ne change rien : les noms TAUX et taux sont strictement équivalent et référent au même attribut.

### Type

En plus de préciser le nom de l'attribut, il faut aussi préciser son type. SQL ne permet de définir que quelques types bien précis, qui sont fréquemment rencontré dans les autres langages de programmation. Cela comprend les booléens, les nombres entiers et à virgule, les chaînes de caractères et quelques autres. À cela, il faut ajouter les données temporelles, comme des dates, des intervalles de temps, etc. Pour simplifier franchement, les types les plus utilisés sont les suivants :
* BOOLEAN pour les booléens ;
* INT pour les nombres entiers ;
* FLOAT pour les nombres à virgule ;
* VARCHAR pour les chaînes de caractères ;
* DATE et DATETIME pour les données temporelles.
Pour les curieux, la liste complète des types supportés est donnée dans le tableau ci-dessous.
Pour préciser le type lors de la déclaration, celui-ci est indiqué à la suite du nom de l'attribut dans la requête CREATE. Un exemple est donné ci-dessous, avec les types des attributs les plus courants.

#### Entiers

L'ensemble des types entiers est détaillé dans le tableau ci-dessous.
| Type | Minimum | Maximum |
| TINYINT | -128 | 127 |
| SMALLINT | -32768 | 32767 |
| MEDIUMINT | -8388608 | 8388607 |
| INT | -2147483648 | 2147483647 |
| BIGINT | 9223372036854775808 | 9223372036854775807 |
On peut préciser qu'on peut placer le mot-clé UNSIGNED devant ces types pour préciser qu'on ne veut mémoriser que des nombres positifs, et pas des nombres négatifs. Dans ce cas, les bornes des types deviennent celles indiquées dans le tableau ci-dessous.
| Type | Minimum | Maximum |
| UNSIGNED TINYINT | 0 | 256 |
| UNSIGNED SMALLINT | 0 | 65536 |
| UNSIGNED MEDIUMINT | 0 | 16777216 |
| UNSIGNED INT | 0 | 4294967296 |
| UNSIGNED BIGINT | 0 | 18446744073709551615 |

#### Nombres à virgule

L'ensemble des types pour les nombres à virgules est détaillé dans le tableau ci-dessous.
| Type | Description |
| NUMERIC et DECIMAL | S'utilise avec deux paramètres : le nombre de chiffres du nombre, et le nombre de chiffres après la virgule. On peut ainsi préciser qu'on souhaite mémoriser un nombre de 16 chiffres, dont maximum 5 après la virgule. DECIMAL fonctionne de la même manière que NUMERIC, mais avec des bornes différentes. |
| FLOAT, DOUBLE et REAL | Mémorisent des nombres à virgule sans qu'on ait à préciser de paramètres. Ces différents types ont des bornes maximales et minimales différentes. |

#### Texte

Pour stocker du texte, on peut utiliser les types ci-dessous. Ces types sont équivalents.
| Type | Description |
| CHAR | On peut préciser en paramètres le nombre d'octets que prendra le texte, maximum 255 octets. |
| VARCHAR | Prend un nombre variable d'octets, le nombre maximal étant précisé en paramètre lors de la définition du type, ce nombre maximal n'allant pas au-delà de 255. Il est plus économe en mémoire que le CHAR pour des textes ou noms de longueur potentiellement inférieure à 255 octets. |
| TEXT | Les limites sont plus grandes que les autres types. |

#### Types temporels

L'ensemble des types temporels est détaillé dans le tableau ci-dessous.
| Type | Description |
| DATE | Mémorise une date au format AAAA-MM-JJ |
| TIME | Mémorise une heure ou un intervalle de temps, au format HH:MM:SS |
| DATETIME | Mémorise une date et une heure (une sorte de fusion entre les types DATE et TIME), au format AAAA-MM-JJ HH:MM:SS |
| YEAR | Ne mémorise qu'une année |
| TIMESTAMP | Durée en seconde écoulée depuis une date définie à l'avance (qui dépend du SGBD) |

#### Autres

Il est possible de définir si une colonne est obligatoire ou facultative. Par obligatoire ou facultative, on veut dire indiquer si la colonne peut contenir la valeur NULL ou non : une colonne obligatoire ne peut pas contenir de NULL alors qu'une colonne facultative le peut. Pour indiquer qu'une colonne est obligatoire, il suffit d'ajouter le mot-clé not null juste après la définition de la colonne. Dans le cas d'une colonne facultative, il ne faut rien faire.

## Clés primaires et étrangères

On sait donc définir des tables, et dire quels sont les attributs que doivent contenir chaque ligne de cette table. Il nous reste cependant à voir comment dire quels sont les attributs qui servent de clé primaire, et ceux qui servent de clé étrangère. De plus, dans le cas des clés étrangères, il faut aussi préciser quelle est la table parent, celle vers laquelle pointent les lignes de la table courante.

### Clé primaire

Pour indiquer quels sont les attributs qui appartiennent à la clé primaire, il faut ajouter quelque chose aux déclarations de la table. Ce quelque chose est situé à la toute fin de la déclaration : il s’agit du mot-clé primary key (clé primaire en anglais), et des noms des attributs de cette clé entre parenthèses.
Pour définir une clé alternative, en plus de la clé primaire, il faut l'indiquer avec le mot-clé unique. On peut déclarer plusieurs clés alternatives, chacune étant définie par un unique séparé des autres. Mais on ne peut définir qu'une seule clé primaire.
On rappelle que les attributs d'une clé primaire ou alternative ne peuvent pas être à NULL.

### Clés étrangères

Pour définir une clé étrangère, on fait comme pour les clés primaires, à ceci près que le mot-clé n'est pas le même : il faut utiliser le mot-clé foreign key (clé étrangère en anglais). De plus, il faut préciser quelle est la table à laquelle cette clé fait référence, ce qui demande d'utiliser le mot-clé reference suivi du nom de la table. Par exemple, voici ce que donnerait une table ENFANT, dont chaque ligne ferait référence à une table MERE et une table PERE.

### Contraintes d'intégrité référentielle

Maintenant, il nous reste à préciser une dernière chose : que faire en cas de suppression d'une ligne dans la table décrite ? On a vu auparavant que l'on pouvait soit interdire la suppression si des clés étrangères pointent encore sur la ligne, mettre à zéro ces clés étrangères, ou bien tout simplement supprimer les lignes qui pointent sur la ligne à supprimer. Pour cela, il faudra utiliser un mot-clé à la fin de la ligne de code qui définit les clés étrangères :
* on delete cascade pour une suppression ;
* on delete no action pour une interdiction de la suppression ;
* on delete set null pour mettre la clé étrangère à NULL.