Быстрый способ проверить две таблицы друг против друга

12

Мы делаем процесс ETL. Когда все сказано и сделано, есть несколько таблиц, которые должны быть идентичными. Какой самый быстрый способ проверить, что эти таблицы (на двух разных серверах) на самом деле идентичны. Я говорю о схеме и данных.

Могу ли я сделать хеш для таблицы сам по себе, как если бы я мог в отдельном файле или файловой группе - сравнить один с другим. Мы сравниваем данные Red-Gate, но так как таблицы содержат миллионы строк в каждой, я бы хотел что-то более производительное.

Один подход, который меня заинтриговал - это творческое использование заявления профсоюза . Но я хотел бы изучить идею хэша немного дальше, если это возможно.

ПОСЛЕ ОТВЕТА ОБНОВЛЕНИЕ

Для любых будущих визитеров ... вот точный подход, который я в итоге выбрал. Это сработало так хорошо, что мы делаем это на каждой таблице в каждой базе данных. Спасибо за ответы ниже, чтобы указать мне в правильном направлении.

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
RThomas
источник
SSIS это вариант? Было бы довольно легко читать в одной таблице и искать другой.
Кевин
1
Это вариант, это то, что используется для процесса ETL, но усы наверху хотят получить второе мнение о том, работает ли он или нет, с помощью SSIS, чтобы доказать, что SSIS понял это правильно, не так убедительно, как отбрасывание таких необычных слов, как CheckSum или MD5 Hash.
RThomas

Ответы:

17

Вот что я сделал раньше:

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

Он работал достаточно хорошо для таблиц, которые содержат около 1 000 000 строк, но я не уверен, насколько хорошо это будет работать с очень большими таблицами.

Добавлено:

Я запустил запрос к моей системе, который сравнивает две таблицы с 21 полем регулярных типов в двух разных базах данных, подключенных к одному и тому же серверу, на котором выполняется SQL Server 2005. Таблица содержит около 3 миллионов строк, и примерно 25000 строк различны. Однако первичный ключ в таблице странный, так как это составной ключ из 10 полей (это таблица аудита).

Планы выполнения запросов имеют общую стоимость 184,25879 для UNIONи 184,22983 для UNION ALL. Стоимость дерева отличается только на последнем шаге перед возвратом строк, конкатенации.

На самом деле выполнение любого запроса занимает около 42 с плюс около 3 с для фактической передачи строк. Время между двумя запросами идентично.

Второе дополнение:

На самом деле это очень быстро, каждый из которых работает с 3 миллионами строк примерно за 2,5 секунды:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableA

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableB

Если результаты не совпадают, вы знаете, что таблицы разные. Однако, если результаты действительно совпадают, вы не гарантируете, что таблицы идентичны из-за [крайне маловероятной] вероятности коллизий контрольной суммы.

Я не уверен, как изменения типа данных между таблицами повлияют на этот расчет. Я бы запустил запрос против systemпредставлений или information_schemaпредставлений.

Я пытался выполнить запрос к другой таблице с 5 миллионами строк, и эта строка выполнялась примерно за 5 секунд, поэтому, по-видимому, это в основном O (n).

Кусочки бекона
источник
8

Вот несколько идей, которые могут помочь:

  1. Попробуйте другой инструмент для различий в данных - пробовали ли вы набор инструментов Idera для сравнения SQL или ApexSQL Data Diff . Я понимаю, что вы уже заплатили за RG, ​​но вы все равно можете использовать их в пробном режиме, чтобы выполнить работу;).

  2. Разделяй и властвуй - как насчет разделения таблиц на 10 небольших таблиц, которые могут быть обработаны каким-либо коммерческим инструментом сравнения данных?

  3. Ограничьте себя только некоторыми столбцами - вам действительно нужно сравнивать данные во всех столбцах?

Марк Дэвидсон
источник
7

Я считаю, что вы должны изучить BINARY_CHECKSUM, хотя я бы выбрал инструмент Red Gate:

http://msdn.microsoft.com/en-us/library/ms173784.aspx

Что-то вроде этого:

SELECT BINARY_CHECKSUM(*) from myTable;
TelegraphOperator
источник
Будет ли это определять различия в схеме таблиц (разные имена столбцов или типы данных)?
ypercubeᵀᴹ
3

Если у вас есть первичный ключ, иногда это лучший способ проверить различия, потому что строки, которые должны быть одинаковыми, отображаются вместе.

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.*
   );

Смотрите это в sqlfiddle .

ErikE
источник