0

我有一组存储过程。每个存储过程都应该使特定的数据库表与另一个数据库中的相同表保持同步。

数据库表有多达数亿条记录。我需要找到最快的方法来验证这些过程是否确实使所有内容保持同步,并且我需要能够找到每个过程的两个表之间不同的记录(用于调试目的)。

我被告知以下内容(我相信在 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 以用于散列目的,因此即使我没有跟踪其余字段,我至少可以跟踪主键并找到多余/丢失的主键)。

4

2 回答 2

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在其计算中忽略不可比较数据类型的列。不可比较的数据类型包括textntextimagecursorxml和不可比较的公共语言运行时 (CLR) 用户定义类型。

于 2012-05-29T13:50:54.133 回答
1

这些是我为此目的使用的 2 个查询

表校验和

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

行校验和

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

学分转到SQL Server 的隐藏功能

于 2012-05-29T13:34:45.463 回答