CREATE TABLE en SQL : Types et Contraintes

Créer une table, choisir les bons types, PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE, CHECK et DEFAULT

9
Sections
20+
Exemples
DDL
Standard

SECTION 01

Syntaxe CREATE TABLE

CREATE TABLE nom_table (
colonne1 type contraintes,
colonne2 type contraintes,

contraintes_de_table
);

— Exemple simple
CREATE TABLE clients (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
age INT CHECK (age >= 0 AND age <= 150),
ville VARCHAR(100) DEFAULT ‘Paris’,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

AUTO_INCREMENT est la syntaxe MySQL. En PostgreSQL, utilise SERIAL ou GENERATED ALWAYS AS IDENTITY. En SQL Server, IDENTITY(1,1). En SQLite, INTEGER PRIMARY KEY suffit.

SECTION 02

Types numériques

Type Taille Plage Usage
TINYINT 1 octet -128 à 127 Booléens, petits compteurs
SMALLINT 2 octets -32 768 à 32 767 Codes, petites quantités
INT / INTEGER 4 octets -2,1 milliards à 2,1 milliards IDs, compteurs, quantités
BIGINT 8 octets ±9,2 × 10¹⁸ IDs haute volumétrie, timestamps
DECIMAL(p,s) Variable Exact Montants, prix (pas de perte)
FLOAT 4 octets Approx. 7 chiffres Calculs scientifiques
DOUBLE 8 octets Approx. 15 chiffres Calculs scientifiques précis
— Prix : TOUJOURS DECIMAL, jamais FLOAT
prix DECIMAL(10,2) — 10 chiffres total, 2 après la virgule
— max : 99 999 999,99

— ID : INT ou BIGINT selon le volume
id BIGINT PRIMARY KEY AUTO_INCREMENT

⚠️ N’utilise jamais FLOAT ou DOUBLE pour les montants financiers. 0.1 + 0.2 ≠ 0.3 en virgule flottante. DECIMAL est exact — c’est le seul choix pour l’argent.

SECTION 03

Types texte

Type Taille max Usage
CHAR(n) 255 caractères Longueur fixe (codes pays, codes postaux)
VARCHAR(n) 65 535 caractères (MySQL) Longueur variable (noms, emails, URLs)
TEXT 65 535 caractères Textes longs (descriptions, commentaires)
MEDIUMTEXT 16 Mo Articles, contenu HTML
LONGTEXT 4 Go Très gros textes (rarement utilisé)
ENUM(‘a’,’b’,’c’) Valeurs prédéfinies (statut, rôle)
— Exemples courants
nom VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
code_postal CHAR(5), — toujours 5 caractères
bio TEXT,
statut ENUM(‘actif’,‘inactif’,‘suspendu’) DEFAULT ‘actif’,

Préfère VARCHAR dans la majorité des cas. CHAR est utile uniquement pour les colonnes de longueur fixe (codes ISO, hashes). TEXT ne peut pas avoir de valeur DEFAULT ni être utilisé comme index complet en MySQL.

SECTION 04

Types date et autres

Type Format Usage
DATE YYYY-MM-DD Dates sans heure (naissance, échéance)
TIME HH:MM:SS Heure seule
DATETIME YYYY-MM-DD HH:MM:SS Date + heure (sans fuseau)
TIMESTAMP YYYY-MM-DD HH:MM:SS Date + heure (avec fuseau UTC)
BOOLEAN TRUE / FALSE Drapeaux (est_actif, est_vérifié)
JSON Texte JSON Données semi-structurées (MySQL 5.7+, PG)
UUID 128 bits Identifiants distribués (PostgreSQL natif)
— Dates
date_naissance DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

— Booléens
est_actif BOOLEAN DEFAULT TRUE,

— JSON (MySQL 5.7+ / PostgreSQL)
metadata JSON,

TIMESTAMP stocke en UTC et convertit selon le fuseau du serveur. DATETIME stocke la valeur telle quelle. Pour les apps internationales, préfère TIMESTAMP (ou TIMESTAMPTZ en PostgreSQL).

SECTION 05

Contraintes

Contrainte Rôle Exemple
PRIMARY KEY Identifiant unique de chaque ligne id INT PRIMARY KEY
NOT NULL Interdit les valeurs NULL nom VARCHAR(100) NOT NULL
UNIQUE Interdit les doublons email VARCHAR(255) UNIQUE
DEFAULT Valeur par défaut si non fournie role VARCHAR(20) DEFAULT ‘user’
CHECK Validation sur les valeurs CHECK (age >= 0)
FOREIGN KEY Lien vers une autre table FOREIGN KEY (client_id) REFERENCES clients(id)
AUTO_INCREMENT Incrémentation automatique id INT AUTO_INCREMENT
— Contrainte composite (UNIQUE sur plusieurs colonnes)
CREATE TABLE inscriptions (
etudiant_id INT NOT NULL,
cours_id INT NOT NULL,
date_inscr DATE NOT NULL,
PRIMARY KEY (etudiant_id, cours_id), — clé primaire composite
UNIQUE (etudiant_id, cours_id) — un étudiant ne s’inscrit qu’une fois
);

SECTION 06

FOREIGN KEY — clés étrangères

— La table enfant référence la table parent
CREATE TABLE commandes (
id INT PRIMARY KEY AUTO_INCREMENT,
client_id INT NOT NULL,
produit VARCHAR(200) NOT NULL,
montant DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE CASCADE — supprimer le client → supprime ses commandes
ON UPDATE CASCADE — modifier l’id client → met à jour ici
);

Action ON DELETE / ON UPDATE Comportement
CASCADE Supprime / met à jour les lignes enfants automatiquement
SET NULL Met la FK à NULL (la colonne ne doit pas être NOT NULL)
SET DEFAULT Met la FK à sa valeur DEFAULT
RESTRICT Empêche la suppression / modification du parent
NO ACTION Comme RESTRICT (vérifie en fin de transaction)

Règle courante : ON DELETE CASCADE pour les tables de détail (commandes d’un client supprimé). ON DELETE SET NULL quand la relation est optionnelle. ON DELETE RESTRICT pour les données critiques (empêcher les suppressions accidentelles).

SECTION 07

ALTER TABLE — modifier une table

— Ajouter une colonne
ALTER TABLE clients ADD COLUMN telephone VARCHAR(20);

— Modifier le type d’une colonne
ALTER TABLE clients MODIFY COLUMN nom VARCHAR(200) NOT NULL; — MySQL
ALTER TABLE clients ALTER COLUMN nom TYPE VARCHAR(200); — PostgreSQL

— Supprimer une colonne
ALTER TABLE clients DROP COLUMN telephone;

— Ajouter une contrainte
ALTER TABLE commandes
ADD CONSTRAINT fk_client
FOREIGN KEY (client_id) REFERENCES clients(id);

— Renommer une table
ALTER TABLE clients RENAME TO utilisateurs;

— Supprimer une table (⚠️ irréversible)
DROP TABLE clients; — erreur si la table n’existe pas
DROP TABLE IF EXISTS clients; — pas d’erreur

SECTION 08

Exemple complet : e-commerce

CREATE TABLE clients (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE produits (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(200) NOT NULL,
prix DECIMAL(10,2) NOT NULL CHECK (prix > 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
categorie VARCHAR(100)
);

CREATE TABLE commandes (
id INT PRIMARY KEY AUTO_INCREMENT,
client_id INT NOT NULL,
statut ENUM(‘en_attente’,‘payee’,‘expediee’,‘livree’,‘annulee’)
DEFAULT ‘en_attente’,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE
);

CREATE TABLE lignes_commande (
id INT PRIMARY KEY AUTO_INCREMENT,
commande_id INT NOT NULL,
produit_id INT NOT NULL,
quantite INT NOT NULL CHECK (quantite > 0),
prix_unit DECIMAL(10,2) NOT NULL,
FOREIGN KEY (commande_id) REFERENCES commandes(id) ON DELETE CASCADE,
FOREIGN KEY (produit_id) REFERENCES produits(id) ON DELETE RESTRICT
);

SECTION 09

Questions fréquentes

VARCHAR(255) : pourquoi 255 partout ?
C’est une convention historique : en MySQL, la longueur d’un VARCHAR est stockée sur 1 octet si ≤ 255, et sur 2 octets si > 255. Économie d’1 octet par ligne. En pratique, choisis une taille qui a du sens métier : 100 pour un nom, 255 pour un email, 20 pour un code postal.
INT ou BIGINT pour les IDs ?
INT supporte jusqu’à ~2,1 milliards — suffisant pour la majorité des tables. BIGINT est nécessaire pour les tables à très haute volumétrie (logs, événements, analytics). En cas de doute, commence par INT — tu peux migrer vers BIGINT plus tard.
CHAR vs VARCHAR ?
CHAR(n) réserve toujours n caractères (complété par des espaces). VARCHAR(n) stocke uniquement les caractères réels + 1-2 octets pour la longueur. Utilise CHAR pour les données de longueur fixe (codes ISO 2 lettres, codes postaux) et VARCHAR pour tout le reste.
CREATE TABLE IF NOT EXISTS ?
Oui, CREATE TABLE IF NOT EXISTS nom (…) ne fait rien si la table existe déjà, au lieu de renvoyer une erreur. Utile pour les scripts d’initialisation de base de données et les migrations.

CREATE TABLE en SQL — Types de données et contraintes

Référence : sql.sh CREATE TABLE