đŸ—ƒïž
SGBDR
  • SGBDR
  • Introduction
    • Introduction
    • Abstractions, DDL, DML et SQL
  • Setup initial
    • Options d'architecture
    • MariaDB via Docker (en dev)
    • Connexion
    • Import des donnĂ©es
    • SĂ©curisation et privilĂšges
  • Interrogation
    • Vocabulaire
    • La base "SaaS"
    • select
    • OpĂ©rations de set
    • AgrĂ©gation
    • Sous-requĂȘtes
    • Jointures
    • Pagination
  • Projet
    • Projet 1 : Pagination
  • Data modeling
    • Introduction
    • Design conceptuel
    • Design logique
    • DĂ©pendances fonctionnelles
    • Normalisation
  • Data dĂ©finition (DDL)
    • Introduction
    • Create table
    • Alter table
    • Identifiants
    • Types complexes
    • Exercice
  • Data manipulation (DML)
    • Update et delete
    • Transactions
    • Stored procedures
  • OpĂ©rations
    • Docker en opĂ©ration
    • Optimisation
    • Sauvegardes
  • Conclusion
    • Conclusion
  • Copyright Kevin Glass 2023
Propulsé par GitBook
Sur cette page
  • IntĂ©gritĂ© : Contrainte par type de donnĂ©es
  • Exercice
  • IntĂ©gritĂ© : Contraintes sur le contenu des colonnes
  • Exercice
  • IntĂ©gritĂ© : Contraintes par la clĂ© primaire
  • Exercice
  • IntĂ©gritĂ© rĂ©fĂ©rentielle
  • Exercice
  • Cascades
  • Exercice
  • IntĂ©gritĂ© vs Cascades vs Null vs Default
  1. Data définition (DDL)

Create table

CrĂ©er une table consiste de l’expression create table suivi par le nom de la table et ensuite, entre parenthĂšses, la liste d’attributs et leurs contraintes.

create table films (    
  film_id int,
  title varchar(255),
  rental_rate numeric(5,2),
  primary key (film_id)
);

En créant nos tables, on essaie au maximum d'assurer les domaines exprimés dans notre schéma avec un objectif ultime : assurer l'intégrité de nos données !

  • On ne veut pas de valeurs non valides dans la base

  • On veut forcer la prĂ©sence de certaines valeurs, ou savoir explicitement quand il y a des valeurs manquantes

  • On veut forcer l'unicitĂ© de certaines valeurs

  • Quand il y a un lien entre deux tables, il faut que ce lien soit assurĂ© et qu'il n'est pas cassable par erreur !

On assure l'intégrité de nos données via les contraintes.

Intégrité : Contrainte par type de données

Nous imposons une premiÚre contrainte sur le type de chaque attribut (ou colonne) de notre table. Cette contrainte est la premiÚre façon d'implémenter la contrainte sur le domaine des valeurs possibles pour un attribut.

Type
Description

char(n)

Une chaĂźne de caractĂšres de longueur fixe. char(8) stockera toujours 8 caractĂšres, mĂȘme si la valeur qu’on passe en a moins (des espaces seront ajoutĂ©s Ă  la fin).

varchar(n)

Une chaĂźne de caractĂšres de longueur variable.

text

Une longue chaßne de caractÚres plus adaptés aux paragraphes etc.

int

Valeurs entiĂšres

numeric(p,d)

Valeurs avec un point décimal fixe : p digits en total, avec d digits aprÚs le point. Ex. numeric(3,2) peut stocker la valeur 2.45 mais pas 24.45

real

Valeurs dĂ©cimales avec la prĂ©cision dĂ©pendant de l’architecture de la machine

double

Valeurs dĂ©cimales avec double prĂ©cision dĂ©pendant de l’architecture de la machine

float(n)

Une valeur décimale avec une précision de n digits.

boolean

Une binaire

date

Une date

datetime

Une date et heure

Cette liste n’est pas exhaustive, et peut changer selon le type de SGBDR choisi.

Testons notre table, en ajoutant quelques lignes, avec la commande insert into :

insert into films (film_id, title, rental_rate)
values (45, "Gone with the wind", 5.99);

La commande insert into a la syntaxe suivant :

insert into ( liste d’attributs Ă  insĂ©rer ) value (la liste de valeurs correspondantes)

On n’est pas obligĂ© d’inclure tous les attributs d’une table :

insert into films (film_id, title)
values (66, "Les bronzés font du ski");

Soit une valeur default est inséré à la place de l'attribut, manquant, soit NULL :

film_id
title
rental_rate

45

Gone with the wind

5.99

66

Les bronzés font du ski

NULL

Exercice

Écrivez et testez la crĂ©ation d’une table pour votre entitĂ© Repas de l’exercice prĂ©cĂ©dente.

  • Vous utilisez quel type de donnĂ©e pour stocker les textes Ă©crit par l’utilisateur ?

  • Vous utilisez quel type de donnĂ©e pour stocker une image ?

Testez l’insertion de quelques donnĂ©es.

Intégrité : Contraintes sur le contenu des colonnes

Par dĂ©faut, chaque attribut autorise les valeurs nulles. On peut encore limite les domaines d'une colonne en enlevant la possibilitĂ© des nulles, prĂ©ciser l’unicitĂ© des valeurs sur la table entiĂšre, ou bien contraindre les valeurs possibles via une condition.

Il y a plusieurs sortes de contraintes sur les colonnes :

  • not null : les valeurs nulles ne sont pas autorisĂ©es

  • unique : les valeurs doivent ĂȘtre uniques sur toutes les lignes de la table

  • check : valider une donnĂ©e selon un critĂšre

Il existe aussi un opérateur default qui permet de fluidifier la contrainte not null, en fournissant une valeur par défaut dans l'absence d'une vraie valeur.

L'exemple suivant démontre les différentes contraintes, ainsi que l'utilisation de default (ici, c'est une table pour la gestion des colis à livrer) :

create table package (
  package_id int NOT NULL UNIQUE,
  name varchar(256) NOT NULL default 'Unknown package',
  price decimal(8,2) NOT NULL default 0,
  article_count int default 1 CHECK (article_count > 0),
  date_of_creation date CHECK (date_of_creation like '__/__/____'),
  primary key (package_id)
);

null vs not null vs default

Attention au piĂšge de default ! On risque de changer le sens d'un attribut en fournissant une valeur default :

  • Est-ce qu'il faut ĂȘtre averti de l'absence d'une information pendant la saisie ? Si oui, il faut utiliser not null seul (sans default). Le SGBDR va lancer une exception, et aucune information inconsistante sera conservĂ©e.

  • Est-ce qu'il faut savoir explicitement quand il a l'absence d'une information ? Si oui, il faut autoriser les null (sans default). On peut ensuite extraire des lignes avec des infos manquantes avec une phrase select et la condition is null, par exemple.

En revanche, si on précise default dans le deux cas dessus, on risque de ne pas savoir si une information a été manquante ou pas, ni extraire des lignes qui manquent une information. On ne peut pas distinguer entre une valeur précisée par default d'une valeur saisie par l'utilisateur.

Exercice

Copiez et exécutez la DDL de cette table « package »

  • Essayez des instructions de type « INSERT »

  • Sans « name », sans « price » ?

    • Avec article_count = 0, < 0 ?

    • Sans date_of_creation ? Est-ce que CHECK suffit Ă  la place de NOT NULL ?

Intégrité : Contraintes par la clé primaire

Nous savons déjà qu'une clé primaire s'agit de notre choix de clé parmi les clés candidates de notre entité. Cette clé doit avoir la caractéristique de pouvoir identifier une ligne unique parmi toutes les lignes de la table.

Désignant une clé primaire, alors, impose implicitement plusieurs contraintes d'intégrité sur la ou les colonnes de la clé :

  • unique : les valeurs dans les attributs de la clĂ© primaire doivent ĂȘtre uniques sur toute la table

  • not null : les valeurs ne peuvent pas ĂȘtre nulls

  • indexation : un index est construit sur ces attributs (notamment pour garantir l'unicitĂ©)

Testons ces rÚgles avec notre table film, en essayant d'ajouter une ligne sans préciser la clé primaire :

insert into films (title)
values ("Jurassic Park");
ERROR 1364 (HY000): Field 'film_id' doesn't have a default value

Nous avons imposé une contrainte type primary key sur la colonne film_id, sans spécifier de valeur default. Nous aurons donc l'erreur affichée.

Rappelez, une clé candidate peut se composer de plusieurs attributs, et donc en SQL, il est possible de créer une clé primaire composée :

create table film_actor (
  film_id int NOT NULL,
  actor_id int NOT NULL,
  primary key (film_id, actor_id)
);

Exercice

Essayez d'ajouter plusieurs combinaisons de lignes Ă  film_actor

  • Avec des valeurs null

  • Avec 2 fois le mĂȘme film_id, mais des actor_id diffĂ©rents

  • Avec 2 fois le mĂȘme film_id et actor_id

Intégrité référentielle

En normalisation notre schéma, nous avons dû couper nos entités en deux.

Les lignes avec un lien sémantique sont reliées par une ou plusieurs colonnes, qu'on appelle les clés étrangÚres.

Rappelez l'exemple de la table etudiant_cours et la table cours :

etudiant_id
cours_id

1

SGBDR

1

Backend

2

Angular

3

Backend

3

Angular

cours_id
prix

SGBDR

1000

Backend

1500

Angular

2000

Ici, l'attribut cours_id de la table etudiant_cours est une clé étrangÚre, car il pointe vers la ligne correspondante dans la table cours.

Ce lien est maintenu strictement par notre SGBDR :

  • on ne peut pas supprimer le cours Angular, par exemple, car la suppression crĂ©era une incohĂ©rence dans nos donnĂ©es. Que deviennent des Ă©tudiants qui Ă  priori sont inscrits du cours Angular, alors que le cours Angular n'existe plus ?

  • on ne peut pas ajouter des Ă©tudiants qui prennent des cours qui ne se trouvent pas dans la table cours. Nous limitons le domaine possible via cette contrainte.

Cette protection implicite dans notre SGBDR s'appelle l'intégrité référentielle.

Mais, cela n'est pas automatique. Nous sommes obligés d'exprimer les clés étrangÚres manuellement :

create table cours (
  cours_id varchar(50) not null,
  prix decimal(8,2),
  primary key (cours_id)
);

create table etudiant_cours (
	etudiant_id int not null,
	cours_id varchar(50) not null,
	primary key(etudiant_id, cours_id),
	foreign key (cours_id) references cours(cours_id)
);

Nous avons ajouté la clause foreign key... :

foreign key (attribut_de_cette_table) references autre_table (attribut_de_l’autre_table)

Exercice

Insérez des données dans cours

  • Essayer d’insĂ©rer des lignes dans etudiant_cours

    • Avec des noms des cours existants

    • Avec des noms des cours qui n’existent pas

  • Essayez de supprimer une ligne de cours avec la ligne suivante :

/* Remplacez le ... par un cours_id d'un des cours existants dans cette table. */
delete from cours where cours_id = '...';

À noter : etudiant_cours est une entitĂ© faible, puisqu'une ligne ne peut pas ĂȘtre créée sans l’existence d’une valeur de cours. C’est grĂące au not null sur cours_id dans la crĂ©ation de etudiant_cours.

Si on autorise les valeurs nulles, etudiant_cours peut exister dans la présence d'un cours, et donc devient une entité forte.

  • Si pas dĂ©jĂ  fait, insĂ©rer un cours avec un cours_id de "SGBDR". Ajouter un Ă©tudiant Ă  ce cours, en crĂ©ant une ligne dans etudiant_cours.

    • Essayez de modifier le nom du cours "SGBDR" Ă  "DBMS". Qu'est-ce qu'il se passe ?

update cours 
set cours_id = "DBMS"
where cours_id = "SGBDR";

L'intégrité référentielle est toujours assurée par le SGBDR !

Nous n'avons pas à faire de pre-tests ou de pre-vérifications de la validité des données. Notre SGBDR s'en occupe tout seul !

Cascades

Il existe des scĂ©narios dans lesquels la modification ou suppression d’une ligne d’une table parent devrait faire modifier/supprimer les lignes dans les tables enfants.

Pour continuer notre exemple, la personne qui a saisi le nouveau cours a fait une faute de frappe, et on a déjà des étudiants inscrits à ce cours. Comment corriger la faute si le SGBDR n'autorise pas la modification ?

Par défaut, notre SGBDR impose des contraintes les plus fortes sur nos références par clé étrangÚre :

  • pas de suppression

  • pas de mise Ă  jour

Nous pouvons modifier ce comportement pour ĂȘtre plus souple, en prĂ©cisant ce qu'il faut faire dans le cas d'une suppression ou modification d'une rĂ©fĂ©rence par clĂ© Ă©trangĂšre.

create table etudiant_cours (
  ...
  foreign key (cours_id) references cours(cours_id) on delete cascade on update cascade
);

Ici, nous spécifions que faire dans le cas d'une suppression ou modification. Il y a trois possibilités :

  • cascade : faire une sorte de propager nos modifications dans les autres tables liĂ©es par la clĂ©-Ă©trangĂšre. Par exemple, toutes les lignes dans etudiant_cours avec "SGBDR" seront modifiĂ©s Ă  "DBMS".

  • set null : Nous cassons les liens existants entre les deux tables, en mettant null dans la clĂ© Ă©trangĂšre (seulement si autorisĂ©)

  • set default : Affecter une valeur par dĂ©faut (en cassant aussi les liens)

Si on oublie de préciser les rÚgles on delete ou on update à la création initiale, il est bien possible de les ajouter plus tard. Nous serions obligés de supprimer la clé étrangÚre, et ensuite la recréer.

Chaque clé (primaire ou étrangÚre) crée implicitement une contrainte dans notre SGBDR, qui lui dispose d'un identifiant. On peut récupérer toutes ses meta-données de nos tables avec la commande suivante :

show create table etudiant_cours;

Le résultat retourné est :

CREATE TABLE `etudiant_cours` (
  `etudiant_id` int(11) NOT NULL,
  `cours_id` varchar(50) NOT NULL,
  PRIMARY KEY (`etudiant_id`,`cours_id`),
  KEY `cours_id` (`cours_id`),
  CONSTRAINT `etudiant_cours_ibfk_1` FOREIGN KEY (`cours_id`) REFERENCES `cours` (`cours_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

On voit dans le résultat la ligne :

CONSTRAINT `etudiant_cours_ibfk_1` FOREIGN KEY (`cours_id`) REFERENCES `cours` (`cours_id`)

C'est la ligne qui met en place la contrainte par clé étrangÚre. Pour la modifier :

/* D'abord, supprimer la clé étrangÚre */
alter table etudiant_cours 
drop foreign key etudiant_cours_ibfk_1;

/* Ensuite la recréer */
alter table etudiant_cours 
add foreign key (cours_id) references cours(cours_id) 
on delete cascade on update cascade;

Exercice

Ajoutez les options de cascade sur la clé étrangÚre sur etudiant_cours.

  • En utilisant update, mettez Ă  jour le nom de SGBDR Ă  DBMS. Qu’est-ce qui se passe ?

  • En utilisant delete, essayez de supprimer la ligne SGBDR. Qu’est-ce qui se passe ?

Intégrité vs Cascades vs Null vs Default

Les cascades sont trĂšs pratiques pour assurer de l’intĂ©gritĂ© automatique sur plusieurs tables. MAIS... le choix n’est pas facile !

S’il est important de garder un historique, les cascades ne sont pas nĂ©cessairement utiles :

  • un Ă©tudiant s’inscrit Ă  un cours et il y participe. On facture sa participation au cours. L’étudiant quitte l’école. Il faut quand mĂȘme le facturer le temps passĂ© au cours
 si on avait fait un ON DELETE CASCADE on n’aurait plus de trace de sa participation, et donc une facturation erronĂ©e

Pour des données « temps réels » les cascades sont trÚs utiles

  • un Ă©tudiant s’inscrit Ă  un cours et il y participe. Il faut gĂ©nĂ©rer la feuille d’émargement L’étudiant quitte l’école. On ne veut plus voir l’étudiant sur la feuille d’émargement, donc un ON DELETE CASCADE serait pratique dans ce cas.

Comment anticiper ces deux scenarii ? Une bonne analyse du domaine (il aura fallu identifier le cas de départ d'un étudiant, ainsi que la cas de facturation prorata), le suivi des procédures de normalisation, etc.

PrécédentIntroductionSuivantAlter table

DerniĂšre mise Ă  jour il y a 1 an

En général, les implémentations de MySQL supporte une liste générique de types :

MariaDB publie une liste de types supportés explicitement par leur SGDBR :

https://www.w3schools.com/sql/sql_datatypes.asp
https://mariadb.com/kb/en/data-types/