mygems

Cómo comparar dos archivos CSV y encontrar qué cambió

diff compara líneas, así que una exportación reordenada parece reescrita. Compara por clave: qué es nuevo, qué desapareció y qué cambió.

Por uos ·

diff old.csv new.csv responde la pregunta equivocada. Compara líneas, así que un archivo ordenado de otra forma sale como "todo cambió", y una fila en la que se corrigió un solo campo sale como una eliminación más una adición, en lugar de como un cambio.

Lo que necesitas es una comparación por clave: qué filas son nuevas, cuáles desaparecieron y, en las filas que están en ambos archivos, qué campos son distintos. Son tres preguntas, y necesitan tres respuestas distintas.

Empieza por la estructura

Antes de comparar el contenido, comprueba que los dos archivos sean comparables:

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

Un encabezado distinto es la causa más común de comparaciones sin sentido. Si se agregó, renombró o reordenó una columna entre las dos exportaciones, toda comparación campo por campo carece de sentido hasta que lo tengas en cuenta. El número de filas es la segunda señal: una diferencia de tres es una semana normal; una diferencia de 40.000 suele significar que una exportación se truncó o se filtró.

La comparación de tres preguntas en SQL

Elige primero tu clave: la columna que identifica el mismo registro en ambos archivos. id, order_id, email. Si no hay ninguna, ve a la última sección.

Filas solo en el archivo nuevo:

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

Filas solo en el archivo anterior:

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

Filas en ambos, con un campo modificado:

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"

Usar IS DISTINCT FROM en lugar de <> es el detalle que importa: <> devuelve NULL cuando cualquiera de los dos lados es NULL, así que un campo que pasó de vacío a lleno, o de lleno a vacío, desaparece de tus resultados sin avisar. Y ese suele ser el cambio que más buscabas.

Para detectar cambios en todas las columnas a la vez sin nombrarlas:

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

EXCEPT compara filas completas e ignora el orden, lo que lo convierte en un diff mucho mejor que diff. Te dice que una fila cambió, no qué campo, así que combínalo con el join anterior cuando ya sepas qué filas mirar.

Cuando los archivos solo están ordenados de otra forma

Si solo necesitas saber si el contenido es el mismo, ordena y calcula el hash:

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

Si el hash es igual, el contenido es el mismo, sin importar el orden de las filas. Si es distinto, no te dice qué cambió, pero tarda un segundo y resuelve la pregunta "¿es la misma exportación?", que muchas veces resulta ser todo el asunto.

Para un diff por líneas que sobreviva al reordenado:

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

Sigue basándose en líneas, así que un cambio de un campo todavía aparece como un par, pero al menos el orden ya no domina la salida.

Herramientas dedicadas

csvdiff y daff comparan ambas por clave y producen una salida que dice "la columna status cambió de A a B en la fila 41" en lugar de mostrarte dos líneas. Si lo haces con regularidad, instala una de las dos. daff diff old.csv new.csv --output diff.html genera una tabla con colores que se lee de un vistazo, algo que ninguna de las opciones anteriores logra.

Git, si los archivos están versionados. Configurar un controlador de diff para CSV en .gitattributes hace que git diff sobre un archivo de datos pase de inútil a útil, y es un cambio de dos líneas.

Excel, siendo honestos, es malo para esto. El enfoque habitual, un BUSCARV (VLOOKUP) por columna hacia la otra hoja, funciona con archivos pequeños, es cuadrático en los grandes y produce una cuadrícula de #N/A difícil de leer. La opción Combinar consultas de Power Query es mucho mejor y casi nadie la usa.

Leer las diferencias en el celular

La versión más común en la vida real: dos exportaciones con una semana de diferencia, en tu correo, y la pregunta es "¿qué cambió?" y no "genera un informe de conciliación".

La vía práctica no es un diff. Agrega una columna que marque el origen, apila los dos archivos, ordena por la clave y lee los pares. Los campos modificados se ven uno al lado del otro porque las dos versiones de cada registro quedan juntas. Para unas pocas docenas de filas, esto es más rápido que cualquier herramienta.

CSV File Viewer - Smart CSViPhone y iPad · Android

SQL directamente sobre el archivo es toda la comparación: un join por la clave con IS DISTINCT FROM encuentra los campos modificados, y el asistente de IA se encarga de la versión de la pregunta que prefieres hacer con palabras en vez de escribirla.

Qué hace

Números, fechas y las diferencias falsas

Una comparación suele arrojar una gran cantidad de "cambios" que no lo son. Descártalos antes de investigar:

  • Espacios al final. "active" y "active " son distintos. Compara valores con trim(), salvo que los espacios tengan significado, lo cual casi nunca ocurre.
  • Formato de números. 1000, 1,000 y 1000.00 son un número y tres cadenas de texto. Convierte ambos lados a un tipo numérico antes de comparar.
  • Formatos de fecha. 01/02/2026 frente a 2026-02-01 y, peor aún, 01/02/2026 significando febrero en un archivo y enero en otro. Convierte ambos a fechas en lugar de comparar texto.
  • Ruido de punto flotante. 0.1 + 0.2 no es 0.3 en ningún lenguaje que uses. Redondea a la precisión que importa antes de comparar decimales.
  • Codificación. Un nombre que se muestra como José en un archivo y como José en el otro es una diferencia de codificación, no un cambio en los datos.

Cada uno de estos puntos produce miles de diferencias en un archivo grande, y así es como una comparación termina "mostrando todo como cambiado".

CSV Editor - Smart CSViPhone y iPad

Cuando la comparación se convierte en una corrección, como normalizar una columna, redondear un decimal o corregir las filas que sí cambiaron, una columna de fórmula la aplica a todas las filas y el resultado se guarda dentro del CSV.

Qué hace

Cuando no hay clave

A veces los archivos no tienen identificador: dos exportaciones de un log, dos listas de transacciones. Tienes dos opciones y ambas son un compromiso.

Construir una clave compuesta con las columnas que juntas identifican una fila: fecha más monto más descripción, por ejemplo. Funciona cuando la combinación realmente es única, y falla en silencio cuando no lo es: dos transacciones idénticas el mismo día se vuelven una, y la comparación reporta una eliminación fantasma.

Comparar como multiconjuntos. Cuenta las apariciones de cada fila distinta en cada archivo y compara los conteos. Es lo que necesitas para logs y transacciones, donde la misma fila aparece legítimamente más de una vez y el cambio relevante es cuántas veces:

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

Ejecútalo en ambos archivos y compara las columnas n. Es el único enfoque que resuelve bien los datos con muchos duplicados.

Preguntas

¿Cómo comparo dos archivos CSV y encuentro las diferencias?
Compara por clave en lugar de por línea. Haz tres preguntas por separado: qué claves están solo en el archivo nuevo, cuáles solo en el anterior y, en las claves presentes en ambos, qué campos son distintos. En SQL son dos anti-joins y un join con `IS DISTINCT FROM` por columna. Un `diff` común reporta un cambio de un solo campo como una eliminación más una adición, y reporta un archivo reordenado como totalmente modificado.
¿Por qué diff dice que cambió cada fila si los datos parecen iguales?
Casi siempre porque las dos exportaciones están ordenadas de forma distinta. `diff` compara las líneas en orden, así que un archivo reordenado parece reescrito por completo. `diff <(sort old.csv) <(sort new.csv)` elimina ese efecto, y `SELECT * FROM old EXCEPT SELECT * FROM new` ignora por completo el orden de las filas.
¿Cómo comparo dos archivos CSV en Excel?
Usa Combinar consultas de Power Query en lugar de BUSCARV (VLOOKUP). Un BUSCARV por columna hacia la otra hoja funciona con unos cientos de filas, es cuadrático en archivos grandes y produce una cuadrícula de #N/A difícil de leer. Power Query hace el join por una clave, mantiene lado a lado las dos versiones de cada columna y se actualiza cuando se reemplazan los archivos.
¿Por qué mi comparación de CSV muestra miles de cambios falsos?
Por diferencias de formato que no son diferencias de datos: espacios al final, separadores de miles, `1000` frente a `1000.00`, dos formatos de fecha, ruido de punto flotante y codificaciones de texto que no coinciden. Normaliza ambos lados (recortar espacios, convertir números a tipo numérico, interpretar las fechas como fechas, redondear decimales) antes de comparar; de lo contrario, cada uno de esos puntos aparece como un cambio en cada fila afectada.
¿Cómo comparo archivos CSV que no tienen columna de ID?
O construyes una clave compuesta con las columnas que juntas identifican una fila, o comparas los archivos como multiconjuntos: cuentas las apariciones de cada fila distinta en ambos lados y comparas los conteos. La segunda opción es la correcta para logs y transacciones, donde la misma fila puede repetirse legítimamente y el cambio que importa es cuántas veces aparece.
¿Cuál es la forma más rápida de saber si dos archivos CSV son idénticos?
Quita el encabezado, ordena y calcula el hash: `tail -n +2 file.csv | sort | md5sum` en cada uno. Hashes iguales significan contenido idéntico sin importar el orden de las filas. No te dice qué es distinto, pero tarda un segundo y muchas veces resuelve la duda que tenías, que era si las dos exportaciones son la misma.

uos — Desarrolla CSV Editor y Smart CSV Viewer, y ha conciliado tantos pares de exportaciones que desconfía de cualquier diff por líneas.