Forma rápida de validar dos tablas una contra la otra


12

Estamos haciendo un proceso ETL. Cuando todo está dicho y hecho, hay un montón de tablas que deberían ser idénticas. ¿Cuál es la forma más rápida de verificar que esas tablas (en dos servidores diferentes) sean de hecho idénticas? Estoy hablando tanto de esquema como de datos.

¿Puedo hacer un hash en la tabla, como lo haría en un archivo individual o grupo de archivos, para comparar uno con el otro? Tenemos una comparación de datos de Red-Gate, pero dado que las tablas en cuestión contienen millones de filas cada una, me gustaría algo un poco más eficiente.

Un enfoque que me intriga es este uso creativo de la declaración del sindicato . Pero, si es posible, me gustaría explorar un poco más la idea del hash.

PUBLICAR ACTUALIZACIÓN DE RESPUESTA

Para cualquier visitante futuro ... aquí está el enfoque exacto que terminé tomando. Funcionó tan bien que lo estamos haciendo en cada tabla de cada base de datos. Gracias a las respuestas a continuación por señalarme en la dirección correcta.

CREATE PROCEDURE [dbo].[usp_DatabaseValidation]
    @TableName varchar(50)

AS
BEGIN

    SET NOCOUNT ON;

    -- parameter = if no table name was passed do them all, otherwise just check the one

    -- create a temp table that lists all tables in target database

    CREATE TABLE #ChkSumTargetTables ([fullname] varchar(250), [name] varchar(50), chksum int);
    INSERT INTO #ChkSumTargetTables ([fullname], [name], [chksum])
        SELECT DISTINCT
            '[MyDatabase].[' + S.name + '].['
            + T.name + ']' AS [fullname],
            T.name AS [name],
            0 AS [chksum]
        FROM MyDatabase.sys.tables T
            INNER JOIN MyDatabase.sys.schemas S ON T.schema_id = S.schema_id
        WHERE 
            T.name like IsNull(@TableName,'%');

    -- create a temp table that lists all tables in source database

    CREATE TABLE #ChkSumSourceTables ([fullname] varchar(250), [name] varchar(50), chksum int)
    INSERT INTO #ChkSumSourceTables ([fullname], [name], [chksum])
        SELECT DISTINCT
            '[MyLinkedServer].[MyDatabase].[' + S.name + '].['
            + T.name + ']' AS [fullname],
            T.name AS [name],
            0 AS [chksum]
        FROM [MyLinkedServer].[MyDatabase].sys.tables T
            INNER JOIN [MyLinkedServer].[MyDatabase].sys.schemas S ON 
            T.schema_id = S.schema_id
        WHERE
            T.name like IsNull(@TableName,'%');;

    -- build a dynamic sql statement to populate temp tables with the checksums of each table

    DECLARE @TargetStmt VARCHAR(MAX)
    SELECT  @TargetStmt = COALESCE(@TargetStmt + ';', '')
            + 'UPDATE #ChkSumTargetTables SET [chksum] = (SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM '
            + T.FullName + ') WHERE [name] = ''' + T.Name + ''''
    FROM    #ChkSumTargetTables T

    SELECT  @TargetStmt

    DECLARE @SourceStmt VARCHAR(MAX)
    SELECT  @SourceStmt = COALESCE(@SourceStmt + ';', '')
            + 'UPDATE #ChkSumSourceTables SET [chksum] = (SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM '
            + S.FullName + ') WHERE [name] = ''' + S.Name + ''''
    FROM    #ChkSumSourceTables S

    -- execute dynamic statements - populate temp tables with checksums

    EXEC (@TargetStmt);
    EXEC (@SourceStmt);

    --compare the two databases to find any checksums that are different

    SELECT  TT.FullName AS [TABLES WHOSE CHECKSUM DOES NOT MATCH]
    FROM #ChkSumTargetTables TT
    LEFT JOIN #ChkSumSourceTables ST ON TT.Name = ST.Name
    WHERE IsNull(ST.chksum,0) <> IsNull(TT.chksum,0)

    --drop the temp tables from the tempdb

    DROP TABLE #ChkSumTargetTables;
    DROP TABLE #ChkSumSourceTables;

END

¿SSIS es una opción? Sería bastante fácil leer en una tabla y buscar en la otra.
Kevin

1
Es una opción, es lo que se usa para el proceso ETL, pero los bigotes de arriba quieren una segunda opinión sobre si funcionó o no, así que usar SSIS para demostrar que SSIS lo hizo bien no es tan convincente como dejar caer palabras elegantes como CheckSum o MD5 Hash.
RThomas

Respuestas:


17

Esto es lo que he hecho antes:

(SELECT 'TableA', * FROM TableA
EXCEPT
SELECT 'TableA', * FROM TableB)
UNION ALL
(SELECT 'TableB', * FROM TableB
EXCEPT
SELECT 'TableB', * FROM TableA)

Funcionó lo suficientemente bien en tablas que tienen aproximadamente 1,000,000 de filas, pero no estoy seguro de qué tan bien funcionaría en tablas extremadamente grandes.

Adicional:

Ejecuté la consulta en mi sistema, que compara dos tablas con 21 campos de tipos regulares en dos bases de datos diferentes conectadas al mismo servidor que ejecuta SQL Server 2005. La tabla tiene aproximadamente 3 millones de filas y hay alrededor de 25000 filas diferentes. Sin embargo, la clave principal de la tabla es extraña, ya que es una clave compuesta de 10 campos (es una tabla de auditoría).

Los planes de ejecución de las consultas tienen un costo total de 184.25879 para UNIONy 184.22983 para UNION ALL. El costo del árbol solo difiere en el último paso antes de devolver filas, la concatenación.

En realidad, ejecutar cualquiera de las consultas toma aproximadamente 42 segundos más 3 segundos para transmitir las filas. El tiempo entre las dos consultas es idéntico.

Segunda adición:

En realidad, esto es extremadamente rápido, cada uno corriendo contra 3 millones de filas en aproximadamente 2.5s:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableA

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableB

Si los resultados no coinciden, sabrá que las tablas son diferentes. Sin embargo, si los resultados hacen partido, que está no garantiza que las tablas son idénticos debido a la posibilidad [altamente improbable] de colisiones de suma de comprobación.

No estoy seguro de cómo los cambios de tipo de datos entre tablas afectarían este cálculo. Ejecutaría la consulta contra las systemvistas o information_schemavistas.

Intenté la consulta en otra tabla con 5 millones de filas y esa se ejecutó en aproximadamente 5 segundos, por lo que parece ser en gran medida O (n).


8

Aquí hay varias ideas que pueden ayudar:

  1. Pruebe diferentes herramientas de diferencia de datos: ¿ha probado el conjunto de herramientas de comparación SQL de Idera o ApexSQL Data Diff ? Me doy cuenta de que ya pagó por RG, pero aún puede usarlos en modo de prueba para hacer el trabajo;).

  2. Divide y vencerás: ¿qué tal dividir tablas en 10 tablas más pequeñas que pueden ser manejadas por alguna herramienta de comparación de datos comerciales?

  3. Limítese solo a algunas columnas: ¿realmente necesita comparar datos en todas las columnas?



3

Si tiene una clave primaria, esta es a veces una mejor manera de examinar las diferencias porque las filas que deberían ser las mismas se muestran juntas.

SELECT
   ID = IsNull(A.ID, B.ID),
   AValue = A.Value,
   BValue = B.Value
FROM
   dbo.TableA A
   FULL JOIN dbo.TableB B
      ON A.ID = B.ID
WHERE
   EXISTS (
      SELECT A.*
      EXCEPT SELECT B.*
   );

Véalo en un sqlfiddle .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.