mygems

Zwei CSV-Dateien vergleichen und herausfinden, was sich geändert hat

diff vergleicht Zeilen, ein neu sortierter Export wirkt daher komplett neu. So vergleichst du stattdessen über den Schlüssel: neu, entfernt, geändert.

Von uos ·

diff old.csv new.csv beantwortet die falsche Frage. Es vergleicht Zeilen. Eine anders sortierte Datei kommt deshalb als „alles geändert“ zurück, und eine Zeile, in der ein einziges Feld korrigiert wurde, erscheint als Löschung plus Ergänzung statt als Änderung.

Gesucht ist ein Vergleich über den Schlüssel: Welche Zeilen sind neu, welche sind weg und, bei Zeilen in beiden Dateien, welche Felder unterscheiden sich? Drei Fragen, und sie brauchen drei verschiedene Antworten.

Zuerst die Struktur prüfen

Bevor du Inhalte vergleichst, kläre, ob die beiden Dateien überhaupt vergleichbar sind:

head -n 1 old.csv new.csv
wc -l old.csv new.csv

Eine geänderte Kopfzeile ist der häufigste Grund für sinnlose Vergleichsergebnisse. Wurde zwischen den beiden Exporten eine Spalte hinzugefügt, umbenannt oder verschoben, ist jeder feldweise Vergleich wertlos, bis du das berücksichtigst. Die Zeilenzahl ist das zweite Signal: Ein Unterschied von drei ist eine normale Woche, ein Unterschied von 40.000 bedeutet meist, dass ein Export abgeschnitten oder gefiltert wurde.

Der Drei-Fragen-Vergleich in SQL

Wähle zuerst den Schlüssel: die Spalte, die denselben Datensatz in beiden Dateien identifiziert. id, order_id, email. Gibt es keine, lies den letzten Abschnitt.

Zeilen nur in der neuen Datei:

duckdb -c "SELECT * FROM read_csv('new.csv') n WHERE n.id NOT IN (SELECT id FROM read_csv('old.csv'))"

Zeilen nur in der alten Datei:

duckdb -c "SELECT * FROM read_csv('old.csv') o WHERE o.id NOT IN (SELECT id FROM read_csv('new.csv'))"

Zeilen in beiden Dateien mit geändertem Feld:

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 statt <> ist das Detail, auf das es ankommt: <> liefert NULL, wenn eine der beiden Seiten NULL ist. Ein Feld, das von leer zu gefüllt oder von gefüllt zu leer wurde, fällt dadurch stillschweigend aus deinen Ergebnissen heraus. Genau diese Änderung hast du vermutlich gesucht.

So erfasst du Änderungen in allen Spalten auf einmal, ohne sie einzeln zu nennen:

duckdb -c "SELECT * FROM read_csv('old.csv') EXCEPT SELECT * FROM read_csv('new.csv')"

EXCEPT vergleicht ganze Zeilen und ignoriert die Reihenfolge. Das ist ein deutlich besseres diff als diff. Es sagt dir, dass sich eine Zeile geändert hat, aber nicht welches Feld. Kombiniere es daher mit dem Join oben, sobald du weißt, welche Zeilen du dir ansehen musst.

Wenn die Dateien nur anders sortiert sind

Willst du nur wissen, ob der Inhalt derselbe ist, sortiere und hashe:

tail -n +2 old.csv | sort | md5sum
tail -n +2 new.csv | sort | md5sum

Gleicher Hash heißt gleicher Inhalt, unabhängig von der Zeilenreihenfolge. Ein unterschiedlicher Hash sagt nichts darüber, was sich geändert hat. Aber es dauert eine Sekunde und klärt die Frage „Ist das derselbe Export?“, die oft schon die ganze Untersuchung ist.

Für einen zeilenweisen Diff, der Umsortierungen übersteht:

diff <(sort old.csv) <(sort new.csv)

Das ist weiterhin zeilenbasiert, eine Änderung in einem Feld erscheint also weiterhin als Paar, aber wenigstens dominiert die Sortierung nicht mehr die Ausgabe.

Spezialwerkzeuge

csvdiff und daff vergleichen beide über den Schlüssel und geben Ausgaben wie „Spalte status hat sich in Zeile 41 von A zu B geändert“ aus, statt dir zwei Zeilen zu zeigen. Wenn du das regelmäßig tust, installiere eines davon. daff diff old.csv new.csv --output diff.html erzeugt eine farbige Tabelle, die du auf einen Blick lesen kannst, was keine der obigen Varianten schafft.

Git, wenn die Dateien versioniert sind. Mit einem CSV-Diff-Treiber in .gitattributes wird git diff auf einer Datendatei von nutzlos zu nützlich, und es sind nur zwei Zeilen Änderung.

Excel ist ehrlich gesagt schlecht darin. Der übliche Weg, ein SVERWEIS (VLOOKUP) pro Spalte ins andere Blatt, funktioniert bei kleinen Dateien, wird bei großen quadratisch langsam und erzeugt ein Raster aus #NV, das schwer zu lesen ist. Das Zusammenführen in Power Query ist viel besser, und fast niemand nutzt es.

Unterschiede auf dem Smartphone lesen

Die häufigste Variante in der Praxis: zwei Exporte im Abstand einer Woche, in deinem E-Mail-Postfach, und die Frage lautet „Was hat sich geändert?“ und nicht „Erstelle einen Abstimmungsbericht“.

Der praktische Weg ist gar kein Diff. Füge eine Spalte hinzu, die die Quelle markiert, stapele die beiden Dateien, sortiere nach dem Schlüssel und lies die Paare. Die geänderten Felder stehen nebeneinander, weil die beiden Versionen jedes Datensatzes jetzt direkt aufeinanderfolgen. Bei ein paar Dutzend Zeilen geht das schneller als mit jedem Werkzeug.

CSV File Viewer - Smart CSViPhone & iPad · Android

SQL direkt auf die Datei ist der ganze Vergleich: Ein Join über den Schlüssel mit IS DISTINCT FROM findet die geänderten Felder, und der KI-Assistent übernimmt die Variante der Frage, die du lieber in Worten stellst, als sie auszuschreiben.

Was es kann

Zahlen, Datumsangaben und die falschen Unterschiede

Ein Vergleich liefert meist eine große Zahl von „Änderungen“, die keine sind. Schließe sie aus, bevor du etwas untersuchst:

  • Leerzeichen am Ende. "active" und "active " sind verschieden. Vergleiche mit trim() bereinigte Werte, es sei denn, Leerzeichen sind relevant, was fast nie der Fall ist.
  • Zahlenformatierung. 1000, 1,000 und 1000.00 sind eine Zahl und drei Zeichenketten. Wandle beide Seiten in einen numerischen Typ um, bevor du vergleichst.
  • Datumsformate. 01/02/2026 gegenüber 2026-02-01 und, schlimmer, 01/02/2026 in einer Datei für Februar und in einer anderen für Januar. Wandle beide in Datumswerte um, statt Text zu vergleichen.
  • Gleitkomma-Rauschen. 0.1 + 0.2 ist in keiner Sprache, die du benutzt, gleich 0.3. Runde auf die relevante Genauigkeit, bevor du Dezimalzahlen vergleichst.
  • Zeichenkodierung. Ein Name, der in der einen Datei als José und in der anderen als José erscheint, ist ein Kodierungsunterschied und keine Datenänderung.

Jeder dieser Punkte erzeugt bei einer großen Datei Tausende Unterschiede, und so endet ein Vergleich damit, dass „alles als geändert angezeigt wird“.

CSV Editor - Smart CSViPhone & iPad

Wird aus dem Vergleich eine Korrektur, etwa eine Spalte zu normalisieren, eine Dezimalzahl zu runden oder die tatsächlich geänderten Zeilen zu korrigieren, wendet eine Formelspalte sie auf jede Zeile an, und das Ergebnis wird in der CSV gespeichert.

Was es kann

Wenn es keinen Schlüssel gibt

Manchmal haben die Dateien keine Kennung: zwei Exporte eines Logs, zwei Listen von Transaktionen. Dann hast du zwei Möglichkeiten, und beide sind Kompromisse.

Einen zusammengesetzten Schlüssel bilden aus den Spalten, die zusammen eine Zeile identifizieren: etwa Datum plus Betrag plus Beschreibung. Das funktioniert, wenn die Kombination wirklich eindeutig ist, und scheitert unbemerkt, wenn nicht: Zwei identische Transaktionen am selben Tag werden zu einer, und der Vergleich meldet eine Phantom-Löschung.

Als Multimengen vergleichen. Zähle, wie oft jede unterschiedliche Zeile in jeder Datei vorkommt, und vergleiche die Zählungen. Das ist die richtige Methode für Logs und Transaktionen, bei denen dieselbe Zeile legitim mehrfach vorkommt und die relevante Änderung darin besteht, wie oft:

duckdb -c "SELECT *, count(*) n FROM read_csv('old.csv') GROUP BY ALL"

Führe das für beide Dateien aus und vergleiche die n-Spalten. Es ist der einzige Ansatz, der bei Daten mit vielen Duplikaten richtig liegt.

Fragen

Wie vergleiche ich zwei CSV-Dateien und finde die Unterschiede?
Vergleiche über den Schlüssel statt zeilenweise. Stelle drei getrennte Fragen: Welche Schlüssel kommen nur in der neuen Datei vor, welche nur in der alten, und welche Felder unterscheiden sich bei Schlüsseln, die in beiden vorkommen? In SQL sind das zwei Anti-Joins und ein Join mit `IS DISTINCT FROM` pro Spalte. Ein einfaches `diff` meldet eine Änderung in einem Feld als Löschung plus Ergänzung und eine neu sortierte Datei als komplett geändert.
Warum meldet diff, dass sich jede Zeile geändert hat, obwohl die Daten gleich aussehen?
Fast immer, weil die beiden Exporte unterschiedlich sortiert sind. `diff` vergleicht Zeilen der Reihe nach, deshalb sieht eine umsortierte Datei komplett neu geschrieben aus. `diff <(sort old.csv) <(sort new.csv)` beseitigt diesen Effekt, und `SELECT * FROM old EXCEPT SELECT * FROM new` ignoriert die Zeilenreihenfolge ganz.
Wie vergleiche ich zwei CSV-Dateien in Excel?
Nutze das Zusammenführen in Power Query statt SVERWEIS (VLOOKUP). Ein SVERWEIS pro Spalte ins andere Blatt funktioniert bei ein paar hundert Zeilen, wird bei großen Dateien quadratisch langsam und erzeugt ein schwer lesbares Raster aus #NV. Power Query verknüpft über einen Schlüssel, zeigt beide Versionen jeder Spalte nebeneinander und aktualisiert sich, wenn die Dateien ersetzt werden.
Warum zeigt mein CSV-Vergleich Tausende falscher Änderungen?
Es sind Formatunterschiede, die keine Datenunterschiede sind: Leerzeichen am Ende, Tausendertrennzeichen, `1000` gegenüber `1000.00`, zwei Datumsformate, Gleitkomma-Rauschen und nicht zusammenpassende Zeichenkodierungen. Normalisiere beide Seiten (trimmen, Zahlen in einen numerischen Typ umwandeln, Datumsangaben als Datum parsen, Dezimalzahlen runden), bevor du vergleichst. Sonst erscheint jeder dieser Punkte als Änderung in jeder betroffenen Zeile.
Wie vergleiche ich CSV-Dateien ohne ID-Spalte?
Bilde entweder einen zusammengesetzten Schlüssel aus den Spalten, die zusammen eine Zeile identifizieren, oder vergleiche die Dateien als Multimengen: Zähle auf beiden Seiten, wie oft jede unterschiedliche Zeile vorkommt, und vergleiche die Zählungen. Letzteres ist die richtige Antwort für Logs und Transaktionen, bei denen sich dieselbe Zeile legitim wiederholen kann und die relevante Änderung darin besteht, wie oft sie vorkommt.
Wie finde ich am schnellsten heraus, ob zwei CSV-Dateien identisch sind?
Kopfzeile entfernen, sortieren und hashen: `tail -n +2 file.csv | sort | md5sum` für jede Datei. Gleiche Hashes bedeuten identischen Inhalt, unabhängig von der Zeilenreihenfolge. Das sagt nichts darüber, was sich unterscheidet, dauert aber eine Sekunde und klärt oft schon die eigentliche Frage, nämlich ob die beiden Exporte derselbe sind.

uos — Entwickelt CSV Editor und Smart CSV Viewer und hat genug Exportpaare abgeglichen, um jedem zeilenbasierten Diff zu misstrauen.