CREATE TABLE définit un schéma
Dans SQLite, toute donnée structurée vit dans une table, et chaque table commence par une instruction CREATE TABLE. Vous lui donnez un nom, vous listez les colonnes, puis vous y rattachez éventuellement des contraintes. SQLite enregistre le schéma dans le fichier de la base, et la table est prête à l'emploi.
Voici l'exemple minimal qui en dit long :
Trois colonnes, une clé primaire, une contrainte NOT NULL. SQLite a rempli id tout seul parce que c'est une clé primaire entière, et il a accepté NULL pour email sur la deuxième ligne parce que rien ne l'interdisait. Voilà la structure de base — un nom, des colonnes, des contraintes — et tout le reste de cette page n'est qu'une variation autour de ce même squelette.
La syntaxe CREATE TABLE, morceau par morceau
Une définition de colonne suit toujours le même schéma : nom TYPE contrainte contrainte .... Le type reste facultatif dans le SQLite classique (on en reparle dans la page sur l'affinité de type), mais mieux vaut toujours le préciser : c'est plus lisible, et la plupart des outils s'appuient dessus.
Quelques points à garder en tête :
- Les contraintes s'enchaînent avec des espaces :
NOT NULL UNIQUEsurskusignifie que les deux règles s'appliquent. - Le
DEFAULT 1surin_stockpermet à l'INSERTd'ignorer cette colonne. - SQLite utilise
INTEGERpour les booléens — il n'existe pas de typeBOOLEANnatif.0vaut faux,1vaut vrai. - Une virgule traînante après la dernière colonne provoque une erreur de syntaxe. SQL est plus strict que JavaScript sur ce point.
IF NOT EXISTS : éviter le plantage à la réexécution
Lancez un CREATE TABLE sur une base qui contient déjà cette table, et SQLite vous renvoie une erreur :
Erreur : la table users existe déjà
Pratique la première fois, vite pénible à la centième. IF NOT EXISTS transforme l'instruction en no-op quand la table existe déjà :
Le second CREATE TABLE ne fait rien — pas d'erreur, pas de changement de schéma. C'est exactement la forme à privilégier dans le code d'initialisation, les scripts de migration, et partout où le même SQL peut être exécuté plusieurs fois.
Attention cependant : IF NOT EXISTS vérifie uniquement le nom. Si une table portant ce nom existe déjà mais avec des colonnes différentes, SQLite la laisse telle quelle. Il ne va ni « corriger » ni « mettre à jour » le schéma à votre place. C'est précisément le rôle des migrations.
Les contraintes : des règles qui suivent le schéma
Les contraintes vous permettent de déléguer la validation directement à la base de données. Voici les quatre que vous utiliserez en permanence :
PRIMARY KEY— identifie une ligne de manière unique. Plus de détails dans la doc dédiée aux clés primaires.NOT NULL— la colonne doit obligatoirement avoir une valeur.DEFAULT valeur— utilisée quand unINSERTne précise pas la colonne. Ça peut être un littéral ou une expression commedatetime('now').CHECK (expr)— doit s'évaluer à vrai pour chaque ligne.UNIQUE (col, col)— contrainte au niveau de la table qui impose l'unicité sur la combinaison des colonnes.
Les contraintes sont vérifiées à chaque INSERT et UPDATE. Toute ligne qui en viole une est rejetée et l'instruction échoue. Attraper les données invalides au niveau de la base coûte bien moins cher que de les rattraper une fois qu'elles se sont propagées dans toute l'application.
Clés étrangères (foreign keys) en SQLite
Une clé étrangère, c'est une façon de dire « cette colonne pointe vers une ligne d'une autre table ». Elle garantit la cohérence de vos données : impossible de référencer un utilisateur qui n'existe pas, et — avec les bonnes options — supprimer un utilisateur peut entraîner en cascade la suppression de ses commandes.
Un piège SQLite à garder en tête : la vérification des clés étrangères est désactivée par défaut. Il faut exécuter PRAGMA foreign_keys = ON sur chaque connexion qui doit faire respecter les contraintes. La plupart des pilotes applicatifs s'en chargent automatiquement ou exposent un réglage dédié ; si ce n'est pas le cas du vôtre, lancez le pragma juste après la connexion.
Ici, ON DELETE CASCADE signifie que la suppression d'un utilisateur entraîne automatiquement celle de ses posts. Les autres options sont SET NULL, RESTRICT et la valeur par défaut, NO ACTION, qui refuse la suppression tant qu'il reste des enregistrements liés.
CREATE TABLE AS SELECT en SQLite
Il arrive qu'on ait besoin de copier rapidement le résultat d'une requête dans une nouvelle table — pour figer un instantané, faire une sauvegarde, ou se créer une table de travail le temps d'une analyse. C'est exactement le rôle de CREATE TABLE ... AS SELECT :
La nouvelle table reprend les noms de colonnes, les types (au mieux) et les données. Mais ce qu'elle ne reprend pas est tout aussi important : pas de clé primaire, pas de NOT NULL, pas d'index, pas de clés étrangères. C'est un instantané à plat. À voir comme un point de départ pour du travail ponctuel, surtout pas comme un moyen de cloner un vrai schéma.
Pour ne récupérer que la structure sans les données, ajoute WHERE 0 :
Vous obtenez une table vide avec la même structure de colonnes — pratique pour les tables d'archive que vous remplirez plus tard.
Les tables temporaires en SQLite
Une table TEMP ne vit que le temps de la connexion en cours. Tu fermes la connexion, et elle disparaît — pas de nettoyage à faire, aucun schéma résiduel :
Quelques cas où c'est vraiment utile : préparer des lignes pour une requête en plusieurs étapes, stocker des résultats intermédiaires trop touffus pour tenir dans une CTE, ou isoler des données propres à une connexion lors d'une session longue. À noter que CREATE TEMP TABLE et CREATE TEMPORARY TABLE sont strictement équivalents.
On peut aussi la combiner avec AS SELECT : CREATE TEMP TABLE snapshot AS SELECT ... est un grand classique pour figer un jeu de résultats au milieu d'une analyse.
Échapper les noms
La plupart du temps, les noms de colonnes et de tables sont de simples identifiants nus. Mais s'il faut utiliser un mot réservé ou un nom contenant des espaces, on l'entoure de guillemets doubles (la norme SQL) ou de backticks (un héritage de MySQL que SQLite accepte aussi) :
Ça fonctionne, mais ça crée des frictions à chaque fois que vous réutilisez la table. Mieux vaut s'en tenir à des noms simples comme orders, selection, user_id et oublier complètement les guillemets.
Un exemple concret
Pour rassembler tout ça — voici un petit schéma pour une application de tâches, avec IF NOT EXISTS pour qu'on puisse l'exécuter à chaque démarrage :
Voilà un schéma prêt pour la prod : création idempotente, clés étrangères activées, un CHECK qui garde done cohérent, des valeurs par défaut sensées, et des timestamps qui se remplissent tout seuls.
La suite : les types de données
Avec CREATE TABLE, vous pouvez écrire INTEGER, TEXT, REAL… mais SQLite est connu pour sa souplesse côté stockage. La page suivante détaille les cinq classes de stockage que SQLite utilise réellement, et pourquoi le type que vous déclarez n'est pas toujours celui que vous récupérez.
Questions fréquentes
Comment créer une table en SQLite ?
On utilise CREATE TABLE nom (colonne1 TYPE, colonne2 TYPE, ...). Chaque colonne a un nom et un type optionnel, et on peut y ajouter des contraintes comme PRIMARY KEY, NOT NULL ou DEFAULT. L'instruction s'exécute immédiatement et la table est conservée dans le fichier de base de données.
À quoi sert IF NOT EXISTS dans CREATE TABLE ?
CREATE TABLE IF NOT EXISTS nom (...) ne crée la table que si aucune table du même nom n'existe déjà. Sans cette clause, relancer le script sur une base existante déclenche une erreur table already exists. C'est le garde-fou classique pour les scripts de migration et le code de démarrage d'une application.
Peut-on créer une table à partir d'un SELECT en SQLite ?
Oui — CREATE TABLE nouveau_nom AS SELECT ... construit une nouvelle table à partir du résultat d'une requête. La nouvelle table reprend les noms de colonnes et les données, mais ne copie ni les contraintes, ni les clés primaires, ni les index de la source. C'est pratique pour des snapshots ou des tables de travail, pas pour remplacer un vrai schéma.
Quelle est la différence entre une table temporaire et une table classique ?
CREATE TEMP TABLE (ou CREATE TEMPORARY TABLE) crée une table qui n'existe que pour la connexion en cours et disparaît dès qu'on la ferme. Les tables classiques, elles, sont persistées dans le fichier de base. Les tables temporaires sont parfaites pour stocker des résultats intermédiaires sans polluer le schéma.