2012-05-29 5 views
1

У меня есть набор хранимых процедур. Каждая хранимая процедура якобы сохраняет таблицу конкретной базы данных в синхронизации с идентичной в другой базе данных.Эффективно найти различия между двумя таблицами базы данных

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

Я был проинформирован о том, что следующее (нашли где-то на SO я верю, но у меня нет ссылки, как это было некоторое время назад):

Insert into target_table(columns) 
select columns from table1 
except 
select columns from table2 

Insert into target_table(columns) 
select columns from table2 
except 
select columns from table1 

не будет работать достаточно быстро. Может ли кто-нибудь предложить другой способ сделать это, что было бы быстрее - либо с использованием процедур T-SQL, либо даже с внешнего кода на C#? (Я думал, что код C# может позволить мне хранить PK для целей хэширования, чтобы я мог по крайней мере отслеживать первичные ключи и находить, которые были излишними/отсутствующими, даже если я не отслеживал остальные поля).

+0

Использование после запуска. –

+0

Можете ли вы изменить схему, чтобы добавить дополнительный столбец типа контрольной суммы для обоих? –

+0

возможно - что бы вы основали checkun? и что бы вы поставили после триггера? –

ответ

3

довольно трудно сделать это , но вы можете получить пробег из контрольных сумм. Один из подходов состоит в том, чтобы разбить диапазон ключей на несколько поддиапазонов, которые могут быть проверены a) параллельно и/или b) в разные запланированные интервалы. Например:

use master; 
go 

set nocount on; 
go 

if db_id('test') is not null 
begin 
    alter database test set single_user with rollback immediate; 
    drop database test; 
end 
go 

create database test; 
go 

use test; 
go 

create table data (id int identity(1,1) not null primary key, 
    data1 varchar(38), 
    data2 bigint, 
    created_at datetime not null default getdate()); 
go 

declare @i int = 0; 
begin transaction 
while @i < 1000000 
begin 
    insert into data (data1, data2) values (newid(), @i); 
    set @i += 1; 
    if @i % 1000 = 0 
    begin 
     commit; 
     raiserror (N'Inserted %d', 0, 0, @i); 
     begin tran; 
    end 
end 
commit 
raiserror (N'Inserted %d', 0, 0, @i); 
go 

backup database test to disk='c:\temp\test.bak' with init; 
go 

if db_id('copy') is not null 
begin 
    alter database copy set single_user with rollback immediate; 
    drop database copy; 
end 
go 

restore database copy from disk='c:\temp\test.bak' 
with move 'test' to 'c:\temp\copy.mdf', move 'test_log' to 'c:\temp\copy_log.ldf'; 
go 

-- create some differences 
-- 
update test..data set data1 = newid() where id = cast(rand()*1000000 as int) 
update copy..data set data1 = newid() where id = cast(rand()*1000000 as int) 

delete from test..data where id = cast(rand()*1000000 as int); 
insert into copy..data (data1, data2) values (newid(), -1); 


-- do the check 
-- 
declare @id int = 0; 
while @id < 1010000 
begin 
    declare @chk1 int, @chk2 int; 
    select @chk1 = checksum_agg(binary_checksum(*)) from test..data where id >= @id and id < @id + 10000 
    select @chk2 = checksum_agg(binary_checksum(*)) from copy..data where id >= @id and id < @id + 10000 
    if @chk1 != @chk2 
    begin 
     -- locate the different row(s) 
     -- 
     select t.id, binary_checksum(*) as chk 
      from test..data t 
      where t.id >= @id and t.id < @id + 10000 
     except 
     select id, binary_checksum(*) as chk 
      from copy..data c 
      where c.id >= @id and c.id < @id + 10000; 

     select t.id, binary_checksum(*) as chk 
      from copy..data t 
      where id >= @id and id < @id + 10000 
     except 
     select id, binary_checksum(*) as chk 
      from test..data c 
      where c.id >= @id and c.id < @id + 10000; 
    end 
    else 
    begin 
     raiserror (N'Range %d is OK', 0,0, @id); 
    end 
    set @id += 10000; 
end 

Основная проблема заключается в том, что определение различий может быть достигнуто только путем сканирования всех строк, что очень дорого. Используя диапазоны, вы можете представить различные диапазоны, которые будут проверяться по графику вращения. В CHECKSUM_AGG и BINARY_CHECKSUM(*) ограничения, конечно:

BINARY_CHECKSUM игнорирует столбцы несравнимых типов данных в вычислений. Несравнимых типы данных включают в себя текст , NTEXT, изображения, курсора, XML и несравнимое выполнение общего языка (CLR) определяемого пользователь типы.

+0

Спасибо за подробный ответ, очень благодарен :) –

1

Эти 2 запросы, которые я использую для этой цели

Таблица Контрольная сумма

Select CheckSum_Agg(Binary_CheckSum(*)) From Table With (NOLOCK) 

Роу Контрольная сумма

Select CheckSum_Agg(Binary_CheckSum(*)) From Table With (NOLOCK) Where Column = Value 

Кредиты идут на Hidden Features of SQL Server

Смежные вопросы