diff old.csv new.csv répond à la mauvaise question. Il compare des lignes : un
fichier trié autrement ressort donc comme « tout a changé », et une ligne dont un
seul champ a été corrigé ressort comme une suppression suivie d'un ajout, et non
comme une modification.
Ce qu'il vous faut, c'est une comparaison par clé : quelles lignes sont nouvelles, lesquelles ont disparu et, pour celles présentes dans les deux fichiers, quels champs diffèrent. Trois questions, qui demandent trois réponses différentes.
Commencer par la structure
Avant de comparer le contenu, vérifiez que les deux fichiers sont comparables :
head -n 1 old.csv new.csv
wc -l old.csv new.csv
Un en-tête modifié est la cause la plus fréquente de comparaisons absurdes. Si une colonne a été ajoutée, renommée ou déplacée entre les deux exports, toute comparaison champ par champ est sans valeur tant que vous n'en tenez pas compte. Le nombre de lignes est le deuxième indice : un écart de trois est une semaine normale, un écart de 40 000 signifie généralement qu'un export a été tronqué ou filtré.
La comparaison en trois questions, en SQL
Choisissez d'abord votre clé : la colonne qui identifie le même enregistrement
dans les deux fichiers. id, order_id, email. S'il n'y en a pas, passez à la
dernière section.
Lignes présentes uniquement dans le nouveau fichier :
duckdb -c "SELECT * FROM read_csv('new.csv') n WHERE n.id NOT IN (SELECT id FROM read_csv('old.csv'))"
Lignes présentes uniquement dans l'ancien fichier :
duckdb -c "SELECT * FROM read_csv('old.csv') o WHERE o.id NOT IN (SELECT id FROM read_csv('new.csv'))"
Lignes présentes dans les deux, avec un champ modifié :
duckdb -c "SELECT o.id, o.status old_status, n.status new_status FROM read_csv('old.csv') o JOIN read_csv('new.csv') n USING (id) WHERE o.status IS DISTINCT FROM n.status"
IS DISTINCT FROM plutôt que <> est le détail qui compte : <> renvoie NULL
dès que l'un des deux côtés est NULL, si bien qu'un champ passé de vide à rempli,
ou de rempli à vide, disparaît silencieusement de vos résultats. C'est
justement le changement que vous cherchiez le plus probablement.
Pour repérer les changements sur toutes les colonnes d'un coup, sans les nommer :
duckdb -c "SELECT * FROM read_csv('old.csv') EXCEPT SELECT * FROM read_csv('new.csv')"
EXCEPT compare des lignes entières et ignore l'ordre, ce qui en fait un bien
meilleur diff que diff. Il vous dit *qu'*une ligne a changé, pas quel champ :
associez-le donc à la jointure ci-dessus dès que vous savez quelles lignes
regarder.
Quand les fichiers sont simplement triés autrement
Si vous voulez seulement savoir si le contenu est le même, triez et calculez une empreinte :
tail -n +2 old.csv | sort | md5sum
tail -n +2 new.csv | sort | md5sum
Même empreinte, même contenu, quel que soit l'ordre des lignes. Une empreinte différente ne dit rien sur ce qui a changé, mais cela prend une seconde et règle la question « est-ce le même export ? », qui s'avère souvent être toute l'enquête.
Pour un diff par ligne qui résiste au réordonnancement :
diff <(sort old.csv) <(sort new.csv)
C'est toujours basé sur les lignes : un changement d'un seul champ apparaît donc encore comme une paire, mais au moins le tri ne domine plus la sortie.
Outils dédiés
csvdiff et daff comparent tous deux par clé et produisent une sortie du
type « la colonne status est passée de A à B pour la ligne 41 » plutôt que de
vous montrer deux lignes. Si vous le faites régulièrement, installez l'un des
deux. daff diff old.csv new.csv --output diff.html produit un tableau coloré
lisible d'un coup d'œil, ce que rien de ce qui précède ne permet.
Git, si les fichiers sont versionnés. Définir un pilote de diff CSV dans
.gitattributes fait passer git diff sur un fichier de données d'inutile à
utile, et il suffit de deux lignes.
Excel, honnêtement, est mauvais pour cela. L'approche habituelle, une
RECHERCHEV (VLOOKUP) par colonne vers l'autre feuille, fonctionne pour les
petits fichiers, devient quadratique sur les gros et produit une grille de #N/A
difficile à lire. La fusion de Power Query est bien meilleure, et presque
personne ne s'en sert.
Lire les différences sur un téléphone
La version courante dans la vraie vie : deux exports à une semaine d'écart, dans votre messagerie, et la question est « qu'est-ce qui a changé ? » plutôt que « produire un rapport de rapprochement ».
La solution pratique n'est pas un diff du tout. Ajoutez une colonne indiquant la source, empilez les deux fichiers, triez par la clé et lisez les paires. Les champs modifiés sont visibles côte à côte, car les deux versions de chaque enregistrement sont désormais adjacentes. Pour quelques dizaines de lignes, c'est plus rapide que n'importe quel outil.
Le SQL directement sur le fichier suffit pour toute la comparaison : une jointure sur la clé avec IS DISTINCT FROM trouve les champs modifiés, et l'assistant IA prend en charge la version de la question que vous préférez poser avec des mots plutôt que d'écrire.
Ce que ça faitNombres, dates et fausses différences
Une comparaison fait souvent ressortir un grand nombre de « changements » qui n'en sont pas. Écartez-les avant d'enquêter :
- Espaces en fin de valeur.
"active"et"active "diffèrent. Comparez des valeurs passées partrim(), sauf si les espaces ont un sens, ce qui n'arrive presque jamais. - Format des nombres.
1000,1,000et1000.00sont un seul nombre et trois chaînes. Convertissez les deux côtés en type numérique avant de comparer. - Formats de date.
01/02/2026contre2026-02-01et, pire,01/02/2026qui signifie février dans un fichier et janvier dans l'autre. Convertissez les deux en dates plutôt que de comparer du texte. - Bruit des flottants.
0.1 + 0.2ne vaut0.3dans aucun des langages que vous utilisez. Arrondissez à la précision qui compte avant de comparer des décimaux. - Encodage. Un nom qui s'affiche
Josédans un fichier etJosédans l'autre est une différence d'encodage, pas un changement de données.
Chacun de ces points produit des milliers de différences sur un gros fichier, et c'est ainsi qu'une comparaison finit par « tout afficher comme modifié ».
Quand la comparaison débouche sur une correction, comme normaliser une colonne, arrondir un décimal ou corriger les lignes qui ont vraiment changé, une colonne de formule l'applique à chaque ligne, et le résultat est enregistré dans le CSV.
Ce que ça faitQuand il n'y a pas de clé
Parfois les fichiers n'ont aucun identifiant : deux exports d'un journal, deux listes de transactions. Vous avez deux options, et ce sont toutes deux des compromis.
Construire une clé composite à partir des colonnes qui identifient ensemble une ligne : date plus montant plus libellé, par exemple. Cela marche quand la combinaison est réellement unique, et échoue sans bruit quand elle ne l'est pas : deux transactions identiques le même jour n'en font plus qu'une, et la comparaison signale une suppression fantôme.
Comparer comme des multi-ensembles. Comptez les occurrences de chaque ligne distincte dans chaque fichier et comparez les comptes. C'est ce qu'il faut pour les journaux et les transactions, où la même ligne apparaît légitimement plusieurs fois et où le changement significatif est le nombre de fois :
duckdb -c "SELECT *, count(*) n FROM read_csv('old.csv') GROUP BY ALL"
Lancez-le sur les deux fichiers et comparez les colonnes n. C'est la seule
approche qui donne un résultat juste sur des données riches en doublons.