1

我有一个名为 [Sectors] 的表,其中存储了行业部门。[SectorId] 被定义为一个 INT 并且是该表的主键。使用主键在整个数据库中引用这些扇区,但在其他表中此主键没有外键约束。

现在该表中有 2 个扇区需要合并为一个。我们有 Sector X 和 Sector Y。Sector Y 需要合并到 Sector X。所以基本上我需要用对 Sector X 的引用替换其他表中对 Sector Y 的所有引用,然后从 [Sectors ] 桌子。

问题是如果没有外键约束,我最终可能会丢失一些仍然引用 Sector Y 的表。

这样做的最佳方法是什么?

4

4 回答 4

3
SET NOCOUNT ON

DECLARE 
  @SQL AS NVARCHAR(MAX),
  @name AS NVARCHAR(128)

SELECT name
    INTO #tables
    FROM sys.sysobjects AS O
    WHERE EXISTS (SELECT *
                    FROM sys.syscolumns
                    WHERE id = O.id
                      AND name = 'SectorID')

WHILE EXISTS (SELECT * FROM #tables)
BEGIN
    SELECT TOP 1
      @name = name
        FROM #tables

    SET @SQL = 'IF EXISTS (SELECT * FROM ' + @name + ' WHERE SectorID = 2)' + CHAR(13) + CHAR(10)
    SET @SQL = @SQL + 'BEGIN' + CHAR(13) + CHAR(10)
    SET @SQL = @SQL + ' UPDATE ' + @name + ' SET SectorID = 1 WHERE SectorID = 2' + CHAR(13) + CHAR(10)
    SET @SQL = @SQL + 'END' + CHAR(13) + CHAR(10)
    PRINT @SQL

    DELETE
        FROM #tables
        WHERE name = @name
END

DROP TABLE #tables
于 2010-07-30T11:16:48.373 回答
1

如果您没有在所有表中调用字段 SectorID,则可以遍历所有具有整数字段的表并检查是否存在任何“扇区 Y”记录。

您可以通过将 syscolumns 与 sysobjects 连接来做到这一点(对于用户表,WHERE xtype = 'U')。

于 2010-07-30T09:01:42.060 回答
0

on update cascade您可以添加具有语义的外键吗?

关于你的观点

我最终可能会丢失一些仍然引用 Sector Y 的表。

在没有 FK 约束的情况下,SQL Server 也没有神奇的方法可以知道这一点。Information_Schema您可以在视图中或在 Sector 表的数据库依赖项(存储过程、视图)的定义中查找名称相似的列,但这两种方法都不是绝对可靠的。

于 2010-07-30T08:56:17.883 回答
0

我做了一些工作来为非常相似的问题找到解决方案。结果是存储过程获取合并记录的 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
于 2011-06-16T10:07:26.840 回答