我正在开发一个Web应用程序,其中有很多表,但有两个表足以说明我的问题:
我们假设User表有一个主键“UserID”,它是Order表中名为“CreatedBy_UserID”的外键。
在删除用户之前,我想检查一下Order表是否有即将被删除的用户创建的记录。
我知道如果我尝试删除用户会发生SqlException,但是让我们说我想事先检查Order表是否没有该用户创建的任何记录?是否有任何我可以运行的SQL代码,如果正在引用该行,它将检查表的所有外键?
这对我来说通常是有用的代码,因为如果可以检测到用户存在于这些其他表中,我可以完全删除删除选项。
我不想要一个简单的查询(SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == @userID
),因为如果我为Order表创建另一个外键,这将不起作用。相反,我想要一些能够遍历所有外键的东西。
可以这样做吗?
答案 0 :(得分:1)
下面是我过去用来执行此任务的sp的代码(请原谅缩进):
create proc dbo.usp_ForeignKeyCheck(
@tableName varchar(100),
@columnName varchar(100),
@idValue int
) as begin
set nocount on
declare fksCursor cursor fast_forward for
select tc.table_name, ccu.column_name
from
information_schema.table_constraints tc join
information_schema.constraint_column_usage ccu on tc.constraint_name = ccu.constraint_name join
information_schema.referential_constraints rc on tc.constraint_name = rc.constraint_name join
information_schema.table_constraints tc2 on rc.unique_constraint_name = tc2.constraint_name join
information_schema.constraint_column_usage ccu2 on tc2.constraint_name = ccu2.constraint_name
where tc.constraint_type = 'Foreign Key' and tc2.table_name = @tableName and ccu2.column_name = @columnName
order by tc.table_name
declare
@fkTableName varchar(100),
@fkColumnName varchar(100),
@fkFound bit,
@params nvarchar(100),
@sql nvarchar(500)
open fksCursor
fetch next from fksCursor
into @fkTableName, @fkColumnName
set @fkFound = 0
set @params=N'@fkFound bit output'
while @@fetch_status = 0 and coalesce(@fkFound,0) <> 1 begin
select @sql = 'set @fkFound = (select top 1 1 from [' + @fkTableName + '] where [' + @fkColumnName + '] = ' + cast(@idValue as varchar(10)) + ')'
print @sql
exec sp_executesql @sql,@params,@fkFound output
fetch next from fksCursor
into @fkTableName, @fkColumnName
end
close fksCursor
deallocate fksCursor
select coalesce(@fkFound,0)
return 0
end
如果某行有任何外键引用,则会选择值1。
您需要的电话是:
exec usp_ForeignKeyCheck('User','UserID',23)
答案 1 :(得分:0)
没有干净的方法来遍历存在多个FK列的所有FK列。您必须构建一些动态SQL来查询系统表并依次测试每个表。
就个人而言,我不会这样做。我知道我有什么FK:我会轮流测试每个
...
IF EXISTS (SELECT * FROM Order WHERE CreatedBy_UserID == @userID)
RAISERROR ('User created Orders ', 16, 1)
IF EXISTS (SELECT * FROM Order WHERE PackedBy_UserID == @userID)
RAISERROR ('User packed Orders', 16, 1)
...
你不会动态迭代某个用户对象的每个属性,并且通常会测试每个属性吗?你有每个属性的代码
答案 2 :(得分:0)
此代码将为您提供为特定表定义的外键列表:
从sys.objects中选择不同的名称
其中object_id in(从sys.foreign_key_columns中选择constraint_object_id为fk
其中fk.Parent_object_id =(从sys.tables中选择object_id
其中name ='tablename'))
答案 3 :(得分:0)
您可以使用交易进行检查。 我知道它看起来像石斧,但它工作快速而稳定。
private bool TestUser(string connectionString, int userID)
{
var result = true;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
var command = connection.CreateCommand();
var transaction = connection.BeginTransaction();
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText = "DELETE User WHERE UserID = " + userID.ToString();
command.ExecuteNonQuery();
transaction.Rollback();
}
catch
{
result = false;
}
}
return result;
}