Comparar tablas

Comparar los datos entre dos tablas es una necesidad habitual: verificar que una migración fue completa, encontrar diferencias entre un entorno de producción y uno de pruebas, o detectar registros que existen en una tabla pero no en otra. MySQL no tiene un operador EXCEPT o MINUS como otros motores de bases de datos, pero hay varias técnicas efectivas para lograr el mismo resultado.

Encontrar filas que están en una tabla pero no en otra

La técnica más común usa LEFT JOIN con una condición IS NULL para encontrar registros huérfanos:

-- Productos que están en la tabla origen pero no en destino
SELECT o.*
FROM productos_origen o
LEFT JOIN productos_destino d ON o.id = d.id
WHERE d.id IS NULL;
idnombreprecio
45Auriculares Bluetooth89.99
78Funda tablet 10"24.99
112Cable HDMI 2.115.99

Estos son los productos que existen en productos_origen pero faltan en productos_destino. El LEFT JOIN incluye todas las filas de la tabla izquierda, y cuando no hay correspondencia en la derecha, las columnas de la derecha son NULL.

Para encontrar las diferencias en ambas direcciones:

-- Filas que solo están en la tabla origen
SELECT 'solo en origen' AS ubicacion, o.id, o.nombre
FROM productos_origen o
LEFT JOIN productos_destino d ON o.id = d.id
WHERE d.id IS NULL
 
UNION ALL
 
-- Filas que solo están en la tabla destino
SELECT 'solo en destino', d.id, d.nombre
FROM productos_destino d
LEFT JOIN productos_origen o ON d.id = o.id
WHERE o.id IS NULL;
ubicacionidnombre
solo en origen45Auriculares Bluetooth
solo en origen78Funda tablet 10"
solo en destino203Cargador inalámbrico

Comparar con NOT EXISTS

Una alternativa a LEFT JOIN que puede ser más legible:

SELECT *
FROM productos_origen o
WHERE NOT EXISTS (
    SELECT 1
    FROM productos_destino d
    WHERE d.id = o.id
);

El resultado es idéntico al LEFT JOIN ... IS NULL, y en la mayoría de los casos el rendimiento también es equivalente. MySQL suele optimizar ambas formas al mismo plan de ejecución.

Encontrar filas con datos diferentes

Cuando ambas tablas tienen los mismos registros pero con valores distintos en algunas columnas:

SELECT
    o.id,
    o.nombre AS nombre_origen,
    d.nombre AS nombre_destino,
    o.precio AS precio_origen,
    d.precio AS precio_destino
FROM productos_origen o
JOIN productos_destino d ON o.id = d.id
WHERE o.nombre != d.nombre
   OR o.precio != d.precio
   OR o.stock != d.stock;
idnombre_origennombre_destinoprecio_origenprecio_destino
3Google Pixel 8Google Pixel 8 Pro699.00899.00
15Camiseta algodónCamiseta algodón básica24.9924.99

Este enfoque muestra lado a lado los valores que difieren, lo que facilita identificar exactamente qué cambió.

Manejar valores NULL en la comparación

El operador != no funciona con NULL porque NULL != NULL devuelve NULL, no TRUE. Usa el operador null-safe <=> o COALESCE:

SELECT o.id, o.nombre
FROM productos_origen o
JOIN productos_destino d ON o.id = d.id
WHERE NOT (o.nombre <=> d.nombre)
   OR NOT (o.precio <=> d.precio)
   OR NOT (o.descripcion <=> d.descripcion);

El operador <=> devuelve TRUE cuando ambos valores son iguales o ambos son NULL, y FALSE en cualquier otro caso. Negarlo con NOT detecta cualquier diferencia, incluyendo cambios de NULL a un valor o viceversa.

Comparar conteos rápidamente

Para una verificación rápida de alto nivel, compara los conteos de filas:

SELECT
    'origen' AS tabla,
    COUNT(*) AS total_filas
FROM productos_origen
UNION ALL
SELECT
    'destino',
    COUNT(*)
FROM productos_destino;
tablatotal_filas
origen150
destino147

Si los conteos difieren, sabes inmediatamente que hay diferencias. Si son iguales, todavía podrían haber diferencias en los valores individuales.

Comparar usando CHECKSUM TABLE

Para una verificación rápida de integridad, MySQL ofrece CHECKSUM TABLE:

CHECKSUM TABLE productos_origen;
CHECKSUM TABLE productos_destino;
TableChecksum
tienda_mysql.productos_origen2345678901
TableChecksum
tienda_mysql.productos_destino2345678901

Si los checksums coinciden, las tablas son idénticas. Si difieren, hay al menos una diferencia. El checksum no te dice cuál es la diferencia, pero es una forma rápida de confirmar que dos tablas son iguales.

Comparar tablas en servidores diferentes

Cuando las tablas están en servidores diferentes, no puedes hacer un JOIN directo. Usa mysqldump con --skip-extended-insert para generar archivos comparables:

# Exportar ambas tablas con formato comparable
mysqldump --skip-extended-insert --compact --no-create-info \
    -h servidor1 -u admin -p tienda_mysql productos | sort > /tmp/tabla1.sql
 
mysqldump --skip-extended-insert --compact --no-create-info \
    -h servidor2 -u admin -p tienda_mysql productos | sort > /tmp/tabla2.sql
 
# Comparar con diff
diff /tmp/tabla1.sql /tmp/tabla2.sql

El flag --skip-extended-insert genera una sentencia INSERT por fila, lo que hace que diff pueda identificar exactamente qué filas difieren. El sort ordena las líneas para que la comparación no se vea afectada por diferencias en el orden de exportación.

Resumen comparativo de técnicas

TécnicaDetecta faltantesDetecta diferenciasManeja NULLRendimiento
LEFT JOIN ... IS NULLSiNoSiBueno
NOT EXISTSSiNoSiBueno
JOIN + WHERE !=NoSiNecesita <=>Bueno
CHECKSUM TABLESolo igualdad totalSolo igualdad totalSiExcelente
mysqldump + diffSiSiSiLento

La combinación adecuada depende de tu escenario. Para verificaciones rápidas, CHECKSUM TABLE es ideal. Para análisis detallados de diferencias, combina la búsqueda de filas faltantes con la comparación de valores modificados.

Escrito por Eduardo Lázaro