2026-10-01
· Dylan YuCSV vers SQLite : 4 façons d'importer des données CSV dans une base de données
Quatre façons pratiques d'importer un CSV dans SQLite — la CLI sqlite3, DB Browser, DBeaver et pandas — plus les pièges d'encodage, de délimiteur et d'affinité de type qui ruinent silencieusement ton import.
Tu as un CSV. Tu veux le mettre dans SQLite. Ça devrait être un travail de deux minutes, et c'est généralement le cas — jusqu'au moment où ça ne l'est plus.
J'ai importé des CSV dans SQLite plus de fois que je ne peux compter : des tableurs exportés depuis Google Sheets, des dumps de bases de données d'un vieux serveur MySQL d'un client, des fichiers de logs que quelqu'un avait renommés en .csv, et un fichier mémorable qui s'est avéré être de l'UTF-16 avec un BOM et des délimiteurs point-virgule. Chacun d'eux était « juste un CSV ».
Il y a quatre façons raisonnables de faire ça, et celle que tu choisis dépend de deux questions : à quelle fréquence vas-tu le faire, et à quel point le CSV est-il désordonné. Si c'est un one-shot et que le fichier est propre, la CLI sqlite3 le fera en trois lignes. Si tu vas relancer l'import chaque semaine depuis un script, tu veux Python. Si le fichier est en désordre et que tu préfères cliquer plutôt que déboguer, un assistant graphique gagne sa place.
Voici la version courte :
- CLI
sqlite3— le plus rapide pour des fichiers propres et ponctuels. Aucune installation sur macOS. Tout est TEXT sauf si tu dis le contraire. - DB Browser for SQLite — un assistant graphique avec des options de délimiteur, d'encodage et d'en-tête. Un bon compromis.
- DBeaver — l'assistant d'import pour quand le fichier est vraiment coriace, ou quand tu vis déjà dans DBeaver. Excessif pour un fichier simple.
- Python + pandas — la réponse quand l'import doit être reproductible, scriptable ou faire partie d'un pipeline.
Je vais parcourir les quatre avec de vraies commandes, puis couvrir la partie que presque tous les tutoriels sautent : ce qui va réellement de travers, et comment corriger les données après leur arrivée.
Avant d'importer : ce qui va réellement de travers avec les CSV
Le CSV est un format avec une spécification — RFC 4180 — que presque personne ne respecte. C'est la racine de la plupart des souffrances. Voici ce que je rencontre le plus souvent.
Les encodages et le BOM UTF-8
L'échec le plus courant, de loin : un CSV exporté depuis Excel sous Windows est souvent enregistré en UTF-8 avec un BOM (byte order mark), ou parfois en Windows-1252 / Latin-1. Le BOM, ce sont trois octets invisibles au début du fichier. Si ton outil d'import ne les retire pas, l'en-tête de ta première colonne devient \ufeffid au lieu de id, et maintenant chaque requête qui référence id échoue avec « no such column ».
Si tu vois un caractère bizarre comme  ou au début du nom de ta première colonne, c'est le BOM. Dans la CLI sqlite3, tu peux généralement éviter le problème en nettoyant le fichier au préalable. En Python, pandas.read_csv le gère si tu passes encoding="utf-8-sig".
Les encodages non-UTF-8 sont l'autre moitié du problème. Un fichier enregistré en Windows-1252 s'importera, mais les caractères accentués se transforment en mojibake (café au lieu de café). Tu corriges ça en décodant avec le bon encodage, pas en éditant les lignes à la main.
Les délimiteurs autres que la virgule
Malgré leur nom, beaucoup de fichiers « CSV » sont séparés par des points-virgules (courant dans les exports Excel européens, où la virgule est le séparateur décimal), par des tabulations, ou par des barres verticales. La CLI sqlite3 peut gérer ça avec .separator, mais si tu ne remarques pas que le délimiteur est un point-virgule, tu obtiens une seule colonne géante.
Les champs entre guillemets contenant des virgules, des guillemets et des sauts de ligne
C'est là que les parseurs naïfs cassent. Un CSV bien formé met entre guillemets tout champ qui contient une virgule, un guillemet ou un saut de ligne :
id,name,notes
1,"Smith, John","He said ""hello"" and left"
2,Jane,"Line one
Line two"
L'importateur CSV de sqlite3 et pandas gèrent tous les deux ça correctement. Un split(",") dans ton propre script, non. Si un décompte de lignes tombe faux — trop de lignes — c'est presque toujours un saut de ligne intégré dans un champ entre guillemets qu'un parseur naïf a scindé en deux enregistrements.
Les formats de date incohérents
Une colonne, trois formats : 2026-01-15, 01/15/2026 et 15 Jan 2026. SQLite n'a pas de type date natif, donc quoi qu'il arrive atterrit en texte, et alors ORDER BY date trie de façon lexicale et te donne n'importe quoi. Décide d'un seul format (j'utilise ISO 8601, YYYY-MM-DD) et normalise avant ou après l'import.
Les zéros initiaux
Codes postaux, numéros de téléphone, références produit — tout ce qui a un zéro initial. Si ton import déclare la colonne comme INTEGER, 00501 devient 501. Si tu la laisses atterrir en TEXT, le zéro survit. C'est une décision de type que tu dois prendre délibérément.
Les colonnes vides et les lignes blanches
Les virgules finales produisent des colonnes vides en fin de ligne. Les lignes blanches produisent des lignes où chaque valeur est vide. Ni l'un ni l'autre ne déclenche d'erreur — ils polluent juste discrètement ta table. Ça vaut un coup d'œil rapide au fichier avant d'importer.
Le résultat « tout est TEXT »
C'est le gros morceau, et c'est spécifique à SQLite. SQLite est typé dynamiquement. Il n'impose pas les types de colonnes comme le font PostgreSQL ou MySQL. À la place, chaque colonne a une affinité de type — un indice sur la façon dont les valeurs doivent être stockées — et la classe de stockage de chaque valeur est décidée valeur par valeur.
Ce que ça signifie en pratique : quand la CLI sqlite3 importe un CSV, elle crée les colonnes de la table avec aucun type déclaré, sauf si tu as créé la table toi-même au préalable. Une colonne sans type déclaré a une affinité BLOB, et les valeurs y entrent en texte. Donc ta colonne price pleine de valeurs 19.99 est TEXT, ta colonne quantity pleine de 5 est TEXT, et SUM(quantity) peut ne pas faire ce que tu attends parce que les valeurs sont stockées comme des chaînes.
Tu peux quand même exécuter SUM() sur du texte qui a l'air numérique — SQLite le convertira — mais tu perds les garanties, les comparaisons deviennent subtilement bizarres, et tout outil qui lit le schéma ne voit aucun type du tout. La solution est de créer la table toi-même avec les types que tu veux, puis d'importer dedans. Je montre ça plus bas.
Voilà le paysage. Maintenant, les quatre méthodes.
Méthode 1 : la CLI sqlite3
C'est le chemin le plus rapide pour un fichier propre et ponctuel, et c'est mon défaut. D'abord, une note sur son installation : sqlite3 est livré avec macOS, donc tu l'as déjà. Sur Windows, il n'est pas installé par défaut — tu télécharges les outils en ligne de commande depuis sqlite.org/download.html et tu mets sqlite3.exe dans ton PATH. La référence complète de ces dot-commands est sur sqlite.org/cli.html.
La version en deux lignes
Le strict minimum ressemble à ça :
sqlite3 mydb.db
.mode csv
.import people.csv people
C'est tout. .mode csv dit à la CLI de traiter l'entrée et la sortie comme du CSV, et .import file table lit le fichier dans la table nommée people.
Deux pièges, et les deux sont importants :
- La table doit déjà exister.
.importne la créera pas. Sipeoplen'existe pas, tu obtiendras une erreur. (Il y a une subtilité : si le nom de table n'existe pas et que tu importes un fichier, certaines versions la créent — mais la table créée a des colonnes non typées, ce qui est le problème « tout est TEXT ». Ne compte pas là-dessus. Crée la table explicitement.) - L'ordre des colonnes compte, et la ligne d'en-tête est importée comme données.
.importlit chaque ligne comme des données. Si ton CSV a un en-tête, cet en-tête devient ta première ligne sauf si tu le sautes.
Gérer la ligne d'en-tête
La CLI sqlite3 moderne prend en charge une option --skip sur .import :
.mode csv
.import --skip 1 people.csv people
--skip 1 saute la première ligne — exactement ce que tu veux pour un en-tête. (Si tu es sur une version plus ancienne sans --skip, l'astuce classique est d'importer l'en-tête comme une ligne puis de faire DELETE FROM people WHERE rowid = 1;, ou de faire passer le fichier par tail -n +2. Je préfère --skip quand il est disponible.)
L'exemple complet détaillé
Faisons ça proprement, avec un schéma explicite pour que les colonnes aient de vrais types. Suppose que people.csv ressemble à ça :
id,name,email,signup_date,account_balance
1,Alice,alice@example.com,2026-01-15,120.50
2,Bob,bob@example.com,2026-02-03,0
3,"Smith, Carol",carol@example.com,2026-02-20,42.00
Remarque le "Smith, Carol" entre guillemets — cette virgule est à l'intérieur des guillemets et doit rester un seul champ. Voici toute la session :
sqlite3 people.db
-- 1. Create the table with real types BEFORE importing.
CREATE TABLE people (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
signup_date TEXT, -- ISO 8601 date, stored as text
account_balance REAL
);
-- 2. Switch to CSV mode and import, skipping the header row.
.mode csv
.import --skip 1 people.csv people
-- 3. Verify.
SELECT COUNT(*) FROM people; -- expect 3
SELECT * FROM people LIMIT 5;
Parce que j'ai déclaré les types, id est un INTEGER, account_balance est REAL, et signup_date est TEXT contenant une date ISO. Le nom entre guillemets a été importé comme la valeur unique Smith, Carol. Si j'avais laissé la CLI créer la table, chaque colonne aurait été non typée.
Si ton fichier utilise un délimiteur différent, définis-le avant d'importer :
.mode csv
.separator ";"
.import --skip 1 people.csv people
Une note sur l'affinité de type
Même avec des types déclarés, rappelle-toi que les règles d'affinité de SQLite sont des conversions, pas des portes. Une colonne déclarée INTEGER stockera 'abc' en texte si tu l'insères — SQLite ne le rejettera pas. Déclarer des types te donne un stockage correct pour des données bien formées et un comportement correct dans SUM(), AVG(), les comparaisons et les index. Ça ne te donne pas de validation. Si tu as besoin d'une validation stricte, il te faut des contraintes CHECK ou des vérifications au niveau applicatif.
Méthode 2 : DB Browser for SQLite
DB Browser for SQLite est une interface graphique gratuite et open source, et son assistant d'import CSV est le plus convivial de tous pour un fichier ponctuel. J'y recours quand le fichier est petit, que je veux examiner l'aperçu avant de valider, ou que la personne qui demande de l'aide n'est pas à l'aise dans un terminal.
Le déroulé :
- Ouvre ou crée ton fichier
.db. - Va dans Fichier → Importer → Table depuis un fichier CSV…
- Choisis ton CSV. Tu obtiens immédiatement un aperçu avec des menus déroulants pour :
- Séparateur de champs — virgule, point-virgule, tabulation, barre verticale, ou une chaîne personnalisée.
- Caractère de guillemet — généralement
", mais certains exports utilisent'. - Encodage — UTF-8, UTF-16, Latin-1 et d'autres. C'est ici que tu corriges les problèmes de BOM et de mojibake.
- La première ligne contient les noms de colonnes — coche ceci pour utiliser l'en-tête comme noms de colonnes au lieu d'une ligne de données.
- Modifie les noms et types de colonnes dans la grille d'aperçu. C'est la fonctionnalité vedette : tu peux définir
iden INTEGER,balanceen REAL et les dates en TEXT directement ici, pour ne pas tomber dans le résultat « tout est TEXT ». - Nomme la table, clique sur OK, et il crée la table et importe les lignes en une seule étape.
L'avantage par rapport à la CLI, c'est que tu vois ce qui va se passer — le délimiteur, l'encodage et les colonnes analysées sont tous visibles avant que quoi que ce soit ne soit écrit. Si un champ entre guillemets contenant une virgule va poser problème, tu verras le nombre de colonnes devenir incorrect dans l'aperçu.
Le compromis, c'est que c'est très « clic ». Faire le même import chaque semaine via un assistant est fastidieux, et tu ne peux pas le mettre dans un script. Pour ça, tu veux la méthode 4.
Méthode 3 : DBeaver
DBeaver est un client de base de données généraliste — il parle à SQLite, PostgreSQL, MySQL et des dizaines d'autres. Si tu l'utilises déjà pour tes autres bases de données, son assistant d'import CSV signifie que tu n'as rien de nouveau à installer ni à apprendre. Il y a une analyse complète de la façon dont il se compare à un outil SQLite dédié dans ma comparaison DBeaver.
Le déroulé de l'import SQLite :
- Connecte-toi à ta base de données SQLite (DBeaver crée la connexion à partir d'un chemin de fichier
.db). - Fais un clic droit sur la table cible (ou sur la base de données, pour en créer une nouvelle) → Import Data.
- Choisis CSV comme source et pointe vers ton fichier.
- L'assistant te guide à travers le délimiteur, le caractère de guillemet, l'encodage et la ligne d'en-tête, avec un aperçu en direct.
- Fais correspondre les colonnes du CSV aux colonnes de la table, choisis comment gérer les lignes existantes (ajouter, tronquer, etc.), et lance-le.
L'assistant de DBeaver est le plus configurable des trois options graphiques. Il gère bien les encodages bizarres, les délimiteurs personnalisés et le mappage des colonnes, et comme c'est un vrai client SQL, tu peux immédiatement exécuter des requêtes SELECT sur le résultat sans changer d'outil.
Quand c'est excessif : si tu ne touches jamais qu'à SQLite, et seulement occasionnellement, DBeaver est une grosse application basée sur Java avec beaucoup de surface que tu n'utiliseras jamais. Pour un seul CSV propre, ouvrir la CLI sqlite3 et taper deux commandes est plus rapide que l'assistant. Et si ce que tu veux réellement, c'est une visionneuse/éditeur pour la table résultante plutôt qu'un outil d'import, un outil local-first plus léger convient mieux — ce qui est à peu près le manque que l'ensemble de fonctionnalités de BaseVolt vise à combler. Plus là-dessus à la fin.
Méthode 4 : Python + pandas
Si l'import doit se produire plus d'une fois — un rafraîchissement hebdomadaire, une étape de build, une partie d'un job ETL — le scripter est le bon choix. Python avec pandas est l'approche standard, et il gère les cas désordonnés (encodage, délimiteurs, dates, dtypes) bien plus élégamment que la CLI.
Le cœur, ce sont deux appels : read_csv pour charger, to_sql pour écrire.
import sqlite3
import pandas as pd
# 1. Read the CSV. utf-8-sig strips a UTF-8 BOM if present.
df = pd.read_csv(
"people.csv",
encoding="utf-8-sig",
sep=",", # change to ";" for semicolon files
dtype={"id": "Int64"}, # keep id as integer, allow NULLs
parse_dates=["signup_date"],
)
# 2. Normalize text columns (strip stray whitespace).
df["name"] = df["name"].str.strip()
# 3. Write to SQLite. if_exists="replace" rebuilds the table each run;
# use "append" to add rows instead.
with sqlite3.connect("people.db") as conn:
df.to_sql("people", conn, if_exists="replace", index=False)
Quelques choses à savoir sur ce pattern :
if_exists="replace"supprime et recrée la table à chaque exécution — parfait pour un rafraîchissement complet, dangereux si la table contient d'autres données ou est référencée par des clés étrangères. Utilise"append"quand tu ajoutes des lignes. Si tu as besoin que les clés étrangères soient appliquées sur la connexion, faisPRAGMA foreign_keys = ON;juste après la connexion ; vois mon guide des clés étrangères pour comprendre pourquoi c'est désactivé par défaut.- Les dtypes comptent. pandas infère les types, et il fera volontiers d'une colonne
idun float s'il y a des valeurs manquantes, transformant1en1.0. Déclarerdtypeexplicitement évite ça. to_sqlutilise les types déclarés de la table dérivés des dtypes du DataFrame, donc tu obtiens de vraies colonnes INTEGER / REAL / TEXT au lieu du résultat tout-TEXT.- Pour les gros fichiers, ajoute
chunksize=10000àto_sqlpour qu'il écrive par lots au lieu de construire une seule transaction géante en mémoire. - Tu peux aussi lire le CSV par morceaux avec
chunksizesurread_csvsi le fichier ne tient pas en mémoire.
C'est la méthode qui passe à l'échelle : versionne-la, mets-la en CI, relance-la à la demande. Les méthodes assistant et CLI sont pour les humains ; celle-ci est pour les machines.
La comparaison
| Méthode | Idéal pour | Gère les CSV désordonnés | Reproductible | Effort de mise en place |
|---|---|---|---|---|
CLI sqlite3 | Fichiers propres et ponctuels ; imports rapides | Limité (délimiteur et en-tête seulement) | Via des scripts shell | Aucun sur macOS ; téléchargement sur Windows |
| DB Browser for SQLite | Fichiers ponctuels où tu veux un aperçu visuel | Bon (encodage, délimiteur, guillemet, types) | Non — clics manuels | Installer l'interface graphique (~petit téléchargement) |
| DBeaver | Fichiers désordonnés, ou quand tu utilises déjà DBeaver | Solide (mappage complet, encodage, délimiteurs) | Non — assistant manuel | Installer une grosse app Java |
| Python + pandas | Pipelines reproductibles et imports planifiés | Le plus fort (encodage explicite, dtypes, dates) | Oui — entièrement scriptable | Python + pandas installés |
Le schéma : la CLI gagne sur la vitesse pour les fichiers propres, les interfaces graphiques gagnent sur la visibilité pour les cas ponctuels désordonnés, et pandas gagne sur la reproductibilité. Il n'y a pas de réponse unique et meilleure, c'est pourquoi il y a quatre méthodes et non une seule.
Un exemple complet de bout en bout
Assemblons-le avec un petit CSV légèrement désordonné pour que tu voies tout l'arc — du fichier brut à la table vérifiée. Appelons-le orders.csv :
order_id,customer,email,order_date,total,notes
1001,Alice,alice@example.com,2026-03-01,49.99,
1002,"Doe, John",john@example.com,2026-03-02,12.50,"Rush order, ship fast"
1003,Alice,alice@example.com,2026-03-05,0,
Remarque trois subtilités du monde réel : un nom entre guillemets avec une virgule ("Doe, John"), une note entre guillemets avec une virgule, et un champ vide en fin de ligne sur deux lignes. Un split(",") naïf saccagerait les deux champs entre guillemets.
Étape 1 — créer la table avec les types.
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer TEXT NOT NULL,
email TEXT,
order_date TEXT, -- ISO 8601
total REAL,
notes TEXT -- empty string -> NULL we'll fix later
);
Étape 2 — importer.
.mode csv
.import --skip 1 orders.csv orders
Étape 3 — vérifier le schéma. .schema affiche l'instruction CREATE TABLE telle que SQLite la voit, ce qui confirme que tes types ont survécu :
.schema orders
-- CREATE TABLE orders (
-- order_id INTEGER PRIMARY KEY,
-- customer TEXT NOT NULL,
-- email TEXT,
-- order_date TEXT,
-- total REAL,
-- notes TEXT
-- );
Étape 4 — vérifier le nombre de lignes. Tu as importé un fichier de 3 lignes (plus un en-tête que tu as sauté), donc tu attends 3 :
SELECT COUNT(*) FROM orders;
-- 3
Si tu avais oublié --skip 1, ça afficherait 4 et tu aurais une ligne avec order_id = 'order_id'. Cette seule vérification attrape l'erreur d'import la plus courante.
Étape 5 — vérifier ponctuellement les lignes délicates. Confirme que les champs entre guillemets sont passés intacts :
SELECT order_id, customer, total, notes FROM orders;
-- 1001 | Alice | 49.99 | (null)
-- 1002 | Doe, John | 12.50 | Rush order, ship fast
-- 1003 | Alice | 0.0 | (null)
Doe, John et Rush order, ship fast sont chacun une seule valeur — la mise entre guillemets a fonctionné. Et les champs vides en fin de ligne sont arrivés en NULL, ce qui nous amène au nettoyage.
Corriger les données après l'import
L'import est la moitié facile. Voici ce que je fais presque toujours ensuite.
Types : vérifie ce que tu as réellement obtenu
Si tu n'as pas déclaré de types (ou utilisé un outil qui a créé la table pour toi), inspecte-les :
SELECT typeof(order_id), typeof(total) FROM orders LIMIT 1;
Si typeof(total) renvoie 'text', tes nombres sont des chaînes. Plutôt que de tout reconstruire, tu peux souvent simplement recréer la table avec les types et copier — ou, si tu utilises pandas, relancer l'import avec des dtypes explicites.
Les dates
SQLite n'a pas de type date. Les dates vivent en TEXT (ISO 8601 est le choix raisonnable) ou en INTEGER (époque Unix) ou REAL (jour julien). Quel que soit le format, garde-le cohérent. Si une colonne a des formats mélangés, normalise :
-- Rewrite US-style MM/DD/YYYY into ISO YYYY-MM-DD.
UPDATE orders
SET order_date = substr(order_date, 7, 4) || '-' ||
substr(order_date, 1, 2) || '-' ||
substr(order_date, 4, 2)
WHERE order_date LIKE '__/__/____';
Alors ORDER BY order_date trie correctement parce que les chaînes ISO se trient lexicalement. Pour tout ce qui est plus complexe, normalise dans pandas avec parse_dates avant que ça n'atteigne la base de données — c'est moins source d'erreurs que de la chirurgie de chaînes en SQL.
Les NULL par rapport aux chaînes vides
Ça mord les gens constamment. Un CSV avec un champ vide s'importe comme une chaîne vide '', pas comme NULL. Ce sont des choses différentes : WHERE notes IS NULL ne les trouvera pas, et COUNT(notes) les compte comme présents.
-- Turn empty strings into real NULLs.
UPDATE orders SET notes = NULL WHERE notes = '';
Fais ça juste après l'import pour que les requêtes en aval et ton outillage de données se comportent bien. (En pandas, df.replace("", pd.NA) avant to_sql fait la même chose.)
La déduplication
Relancer un import est la façon classique d'obtenir des lignes en double. Si tu as une clé naturelle, utilise-la :
-- Remove duplicate order_id rows, keeping the lowest rowid.
DELETE FROM orders
WHERE rowid NOT IN (
SELECT MIN(rowid) FROM orders GROUP BY order_id
);
Mieux : empêche les doublons au niveau du schéma avec une contrainte UNIQUE sur la clé naturelle, et utilise INSERT OR IGNORE lors des imports suivants.
Ajouter une clé primaire
Si ta table vient d'un outil qui l'a créée sans clé primaire, et que tu as une colonne de clé naturelle, tu es dans la même situation que pour ajouter n'importe quelle contrainte à une table SQLite existante : ALTER TABLE ne peut pas ajouter de clé primaire. Tu reconstruis la table — crées-en une nouvelle avec la clé, copies les lignes, supprimes l'ancienne, renommes. (Si tu as une colonne en trop et que tu veux juste une clé de substitution basée sur le rowid, note que chaque table SQLite a déjà un rowid implicite que tu peux utiliser dans les requêtes sans rien déclarer.)
Pour le pattern de reconstruction complet — et pourquoi c'est un processus en plusieurs étapes — vois la section ALTER TABLE dans mon guide des clés étrangères SQLite. Une fois la table propre, si tu veux la parcourir et l'éditer sans écrire plus de SQL, ouvrir le fichier .db dans une vraie visionneuse est l'étape suivante ; mon guide sur comment ouvrir un fichier .db couvre les options.
Dépannage
Les échecs que je vois encore et encore, et ce qu'ils signifient :
- « no such table: X » —
.importexige que la table existe. Crée-la d'abord. C'est l'erreur n°1 de la CLI. - Première colonne nommée
idouid— BOM UTF-8. Réenregistre en UTF-8 sans BOM, ou importe avecencoding="utf-8-sig"en pandas. - Le nombre de lignes est supérieur d'une unité à ce qui est attendu — tu n'as pas sauté l'en-tête. Ajoute
--skip 1(CLI) ou coche « la première ligne contient les noms de colonnes » (GUI). - Tout a atterri dans une seule colonne — mauvais délimiteur. Définis
.separator ";"(ou le bon) avant d'importer, ou choisis-le dans l'assistant. - Le nombre de lignes est bien trop élevé — des sauts de ligne intégrés dans des champs entre guillemets sont scindés par un parseur naïf. Utilise la CLI, un assistant graphique ou pandas — tous gèrent les sauts de ligne entre guillemets ;
split(",")non. - Les caractères accentués ressemblent à
café— mauvais encodage. Importe en UTF-8, ou dans l'encodage dans lequel le fichier a réellement été enregistré (souvent Windows-1252). - Les zéros initiaux ont disparu (
00501→501) — la colonne a été traitée comme INTEGER. Déclare-la TEXT. SUM()renvoie 0 ou n'importe quoi — les valeurs sont stockées en TEXT et quelque chose ne les convertit pas. Déclare des types numériques et réimporte.WHERE x IS NULLne trouve rien — les champs vides sont devenus''. Lance la mise à jour chaîne-vide-vers-NULL.- Les nombres s'affichent en
1.0au lieu de1— pandas a inféré un float à cause des valeurs manquantes. Définisdtype={"id": "Int64"}. Error: FOREIGN KEY constraint failed— si tu importes dans une table avec des contraintes FK et que les clés étrangères sont activées, une ligne parente référencée est manquante. Insère les parents d'abord, ou importe dans l'ordre des dépendances.
Quand utiliser quoi
Un petit guide de décision, dans l'ordre où je déciderais réellement :
- Un fichier, propre, une fois → CLI
sqlite3. Trois commandes, terminé. - Un fichier, désordonné, et je veux le voir avant de valider → DB Browser for SQLite. L'aperçu gagne sa place.
- Je vis déjà dans DBeaver, ou le fichier a besoin d'un vrai mappage de colonnes → l'assistant d'import de DBeaver.
- Je vais refaire ça la semaine prochaine → Python + pandas. Scripte-le, versionne-le, oublie-le.
- J'ai besoin que la table résultante soit parcourable et éditable par un humain → importe avec l'une des méthodes ci-dessus, puis ouvre le
.dbdans un vrai outil.
Ce dernier point est la partie où la plupart des tutoriels s'arrêtent, et c'est généralement le vrai objectif. Tu importes rarement un CSV parce que tu veux un CSV dans une base de données — tu le fais parce que tu veux interroger, filtrer, joindre et éditer les données. La CLI sqlite3 fait entrer les données, mais c'est un mauvais endroit où vivre. C'est là qu'une application desktop local-first comme BaseVolt trouve sa place : pointe-la vers le fichier .db produit par ton import et tu obtiens un tableau de bord complet — vues grille, galerie, kanban et dashboard, plus la visualisation des relations — en quelques secondes, sans aucun changement de schéma. Pour être clair, BaseVolt n'est pas un importateur CSV ; tu fais entrer le CSV avec l'une des quatre méthodes ci-dessus, et BaseVolt est la façon dont tu travailles avec la table ensuite. C'est une app locale pour macOS (Apple Silicon) et Windows, elle se connecte à PostgreSQL, MySQL, SQLite et Cloudflare D1, et tes données ne quittent jamais localhost — tu peux la récupérer depuis la page de téléchargement ou essayer la démo live sur demo.basevolt.app. Pour le côté SQLite spécifiquement, il y a plus sur /sqlite.
En résumé
Importer un CSV dans SQLite est un problème résolu — il y a quatre bonnes façons de le faire, et la bonne dépend de ta situation, pas de quel outil est « le meilleur ».
- La CLI
sqlite3est la plus rapide pour des cas ponctuels propres, mais elle ne créera pas de table typée pour toi et ne sautera pas ton en-tête sauf si tu le lui dis. Crée la table d'abord, puis.mode csvet.import --skip 1. - DB Browser et DBeaver te donnent un aperçu et de vrais contrôles d'encodage/de délimiteur/de type, ce qui compte quand le fichier est désordonné.
- pandas est ce que tu veux dès que l'import doit se produire plus d'une fois.
- Quoi que tu utilises, rappelle-toi l'affinité de type de SQLite : si tu ne déclares pas de types, tout est TEXT. Et les champs CSV vides deviennent des chaînes vides, pas NULL — corrige ça immédiatement.
L'import en lui-même, c'est cinq minutes. Les dix minutes que tu passes à déclarer des types, normaliser les dates et convertir les chaînes vides en NULL font la différence entre une table que tu peux interroger et une table qui ne cesse de te surprendre.
Une fois les données dedans, BaseVolt est la façon dont tu les utilises réellement — un panneau d'administration local-first pour ta base de données SQLite, sans compte requis, sans données quittant ta machine.
Si tu as trouvé ça utile, j'écris sur les bases de données, le logiciel local-first, et les parties peu glamour de la construction d'outils de dev. Retrouvez-moi sur X.