我做了一些工作来为非常相似的问题找到解决方案。结果是存储过程获取合并记录的 ID,首先离开它们,更新相关表中的所有外键并删除其余的。
例如关于 topicstarter 问题。假设我们有这些表:
[Sectors]
ID Name
10 'SectorA'
20 'Sector A'
30 'Sector B'
40 'sector a'
[RelatedRecords]
ID, SectorID, SomeField
1, 10 'value 1'
2, 20 'value 2'
3, 30 'value 3'
4, 40 'value 4'
(ID 必须是主键,SectorID 必须是外键)我们要合并记录 10、20、40,留下记录 20。为此,我们应该调用:
dbo.MergeRecords '20, 10, 40', 'Sectors'
结果将是:
[Sectors]
ID Name
20 'Sector A'
30 'Sector B'
[RelatedRecords]
ID, SectorID, SomeField
1, 20 'value 1'
2, 20 'value 2'
3, 30 'value 3'
4, 20 'value 4'
如果没有相关表,则只执行删除。此解决方案涵盖了您拥有单值主键(我记得是 3NF)的情况。
所以这里是存储过程代码:
-- =============================================
-- Description: Merging table records.
-- First record will be leaved, other will be deleted.
-- Depended foreign keys in all tables will be updated.
-- Example:
-- exec MergeRecords '1, 2, 3', 'SomeRecords'
-- =============================================
CREATE PROCEDURE [dbo].[MergeRecords]
@Id nvarchar(max), -- Comma-separated IDs
@PKTable nvarchar(50) -- Name of a table where merge records in
AS
BEGIN
SET NOCOUNT ON;
declare @PKField nvarchar(50),
@FKTable nvarchar(50),
@FKField nvarchar(50)
declare @updateSql nvarchar(max),
@deleteSql nvarchar(max)
declare @firstId nvarchar(max),
@otherId nvarchar(max)
set @firstId = LEFT(@Id, CHARINDEX(',', @Id) - 1)
set @otherId = RIGHT(@Id, LEN(@Id) - CHARINDEX(',', @Id))
-- Primary key name
select @PKField = ccu.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu on ccu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
where tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
and tc.TABLE_NAME = @PKTable
-- Loop foreign keys
declare constraints_cursor cursor local fast_forward
for select
--tc.CONSTRAINT_NAME,
--ccu_pk.TABLE_NAME PK_TABLE_NAME,
--ccu_pk.COLUMN_NAME PK_COLUMN_NAME,
ccu_fk.TABLE_NAME FK_TABLE_NAME,
ccu_fk.COLUMN_NAME FK_COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc on rc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu_fk on ccu_fk.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu_pk on ccu_pk.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
where ccu_pk.TABLE_NAME = @PKTable
and tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
--Example, @PKTable = 'SomeRecords'
--CONSTRAINT_NAME PK_TABLE_NAME PK_COLUMN_NAME FK_TABLE_NAME FK_COLUMN_NAME
--FK_SomeRecords_SomeRelatedRecords1 SomeRecords Id SomeRelatedRecords FirstSomeRecordId
--FK_SomeRecords_SomeRelatedRecords2 SomeRecords Id SomeRelatedRecords SecondSomeRecordId
--FK_SomeRecords_AnotherRelatedRecords SomeRecords Id AnotherRelatedRecords SomeRecordId
open constraints_cursor
fetch next from constraints_cursor
into @FKTable, @FKField
while @@fetch_status = 0
begin
-- Update foreign keys
set @updateSql = '
update @FKTable
set @FKField = @firstId
where @FKField in (@otherId)'
set @updateSql = replace(@updateSql, '@FKTable', @FKTable)
set @updateSql = replace(@updateSql, '@FKField', @FKField)
set @updateSql = replace(@updateSql, '@firstId', @firstId)
set @updateSql = replace(@updateSql, '@otherId', @otherId)
exec sp_executesql @updateSql
fetch next from constraints_cursor
into @FKTable, @FKField
end
close constraints_cursor
deallocate constraints_cursor
-- Delete other records
set @deleteSql =
'delete from @PKTable
where @PKField in (@otherId)'
set @deleteSql = replace(@deleteSql, '@PKTable', @PKTable)
set @deleteSql = replace(@deleteSql, '@PKField', @PKField)
set @deleteSql = replace(@deleteSql, '@otherId', @otherId)
exec sp_executesql @deleteSql
select 0
END