如何在删除父表记录sql server之前检查外键引用记录

时间:2014-02-04 10:13:18

标签: sql-server-2008 foreign-keys

我必须在从Sql server表中删除记录时执行以下操作。

当我尝试从表中删除记录时(TableA)如果该记录未被任何表引用,那么只有该记录必须被删除,否则如果任何其他表记录引用该记录,我必须更新列必须更新表中的Obsolete(tableA)。

对于这种情况,我想检查该记录是否被同一数据库中的任何其他表引用。

3 个答案:

答案 0 :(得分:0)

请尝试使用下面给出的代码

Create Table Parent
(
    ID INT Identity primary key,
    val varchar(10),
    IsObsolete bit not null default(0)
)

create table child
(
    CID INT Identity,
    ID INT CONSTRAINT [FK_Parent_ID] REFERENCES Parent(ID),
)

INSERT INTO Parent(val) values('a'),('b'),('c')

insert into child(id) values (1),(2)

-- NOW SUPPOSE I WANT TO DELETE RECORD B

IF EXISTS (SELECT * FROM child WHERE id = (select id from parent where val='b'))  -- this exists then update
BEGIN
    UPDATE Parent
    Set IsObsolete = 1
    Where  val='b'
END
ELSE
BEGIN
    DELETE FROM Parent 
    Where  val='b'
END

答案 1 :(得分:0)

我使用以下选择进行了查看:

SELECT        FK_Table, FK_Column, PK_Table, PK_Column, PK_Alias = CASE WHEN PrevVal = PK_Table THEN PK_Table + ltrim(str(RowNumber)) ELSE PK_Table END
FROM            (SELECT        *, ROW_NUMBER() OVER (ORDER BY FK_Table) AS RowNumber, lag(PK_Table) OVER (ORDER BY FK_Table) PrevVal, LEAD(PK_Table) OVER (ORDER 
                         BY FK_Table) NextVal
FROM            (SELECT        TOP (100) PERCENT FK.TABLE_NAME AS FK_Table, CU.COLUMN_NAME AS FK_Column, PK.TABLE_NAME AS PK_Table, 
                                                    PT.COLUMN_NAME AS PK_Column
                          FROM            INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS C INNER JOIN
                                                    INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME INNER JOIN
                                                    INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME INNER JOIN
                                                    INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME INNER JOIN
                                                        (SELECT        i1.TABLE_NAME, i2.COLUMN_NAME
                                                          FROM            INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS i1 INNER JOIN
                                                                                    INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
                                                          WHERE        (i1.CONSTRAINT_TYPE = 'PRIMARY KEY')) AS PT ON PT.TABLE_NAME = PK.TABLE_NAME) x) p

此视图将为我提供所有外键。当我必须多次连接同一张表时(出于其他目的,不在这里),我使用PK_Alias进行联合联接。

现在,我使用这个小的存储过程来返回一个数据集,该数据集包含所有表和行数,其中每个表都有PK引用:

已编辑: 约束 代表视图

create procedure mvp_CountRowsForFKs
@PKColumnName varchar(100),@ColumnValue sysname
as
begin

    set nocount on

    declare cCurs cursor fast_forward for
    select FK_Table,FK_Column
    from Constraints
    where PK_Column=@PKColumnName

    declare @Result table (table_name varchar(100),Counted int)

    declare @TN varchar(100),@CN varchar(100),@Command varchar(300)
    open cCurs
         fetch next from cCurs into @TN,@CN
         while @@FETCH_STATUS = 0
            begin
                set @Command = 'SELECT '''+@TN+''',Count(*) as Counted from '+@TN+' where '+@CN+'='+str(@ColumnValue)   
                insert @Result exec(@Command)
                fetch next from cCurs into @TN,@CN
            end
    close cCurs
    deallocate cCurs

    select table_name,sum(Counted) Counted from @Result  where Counted<>0 group by Table_Name
end

当您在两个或多个FK中使用相同的PK时,该视图将多次为您提供同一FK表,因此,根据FK中记录的存在,@ Result变量表将多次存储同一表。列:)

PS:我想PK列的类型是Integer

使用示例:

exec mvp_CountRowsForFKs 'IdUser',34

它将返回如下内容:

enter image description here

答案 2 :(得分:0)

这是一个老歌,但我遇到了它并认为我会添加到组合中......

如果您应用了适当的外键约束(并且您没有使用 ON DELETE CASCADE),那么如果您尝试删除该行,则会出现异常。现在使用异常来控制正常的执行流程被认为是不好的做法,因为异常很昂贵 - 但数据库命令也是如此!我想不出更好的方法。所以这是我基于 C# 的方法:

try {
    // code to execute the delete here
}
catch (SqlException ex) {
    if (!SqlErrors.IsForeignKeyConstraintError(ex)) {
        throw;
    }
    // could not delete, do something else...
}

为了区分实际的 FK 异常和其他一些 SQL 异常,这是我的类:

using System.Data.SqlClient;
using System.Linq;

namespace My.Funky.Namespace {

    public static class SqlErrors {

        // From https://msdn.microsoft.com/en-us/library/cc645611.aspx
        // also: select * from sys.messages where language_id=1033 and severity between 11 and 16

        private enum Errors {
            None = 0,
            ForeignKeyConstraint = 547,
            RaiseError = 50000
            // ...there are 12K+ messages - so not putting them all here!
        }

        public static bool IsForeignKeyConstraintError(SqlException ex) {
            return ex != null && (from object err in ex.Errors select err as SqlError).Any(err =>
                       err.Number == (int)Errors.ForeignKeyConstraint ||
                       err.Number == (int)Errors.RaiseError
                   );
        }
    }
}

诚然,海报没有提到 C#,但很可能最初的问题现在完全没有实际意义 - 其他人可能会发现它很有用! :o)

(我希望在 T-SQL 中实现类似的东西会很容易)