简体   繁体   English

SQL Server:检查子行是否存在

[英]SQL Server: Check if Child Rows Exist

I am working on a web application where there are many tables but two will suffice to illustrate my problem: 我正在一个Web应用程序上工作,其中有很多表,但是两个表足以说明我的问题:

  1. User 用户
  2. Order 订购

Let us say that the User table has a primary key "UserID", which is a foreign key in the Order table called "CreatedBy_UserID". 假设User表具有主键“ UserID”,这是Order表中名为“ CreatedBy_UserID”的外键。

Before deleting a User, I would like to check if the Order table has a record created by the soon-to-be deleted user. 在删除用户之前,我想检查“订单”表中是否有由即将删除的用户创建的记录。

I know that a SqlException occurs if I try to delete the user but let us say that I want to check beforehand that the Order table does not have any records created by this user? 我知道如果尝试删除用户会发生SqlException,但是让我们说我想事先检查Order表没有该用户创建的任何记录? Is there any SQL code which I could run which will check all foreign keys of a table if that row is being referenced? 如果引用了该行,是否可以运行任何SQL代码来检查表的所有外键?

This for me is generally useful code as I could remove the option for deletion altogether if it can be detected that the user exists in these other tables. 这对我来说通常是有用的代码,因为如果可以检测到用户存在于其他表中,则可以完全删除删除选项。

I don't want a simple query ( SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == @userID ) because this will not work if I create another foreign key to the Order table. 我不需要简单的查询( SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == @userID ),因为如果我为Order表创建另一个外键,这将不起作用。 Instead I want something that will traverse all foreign keys. 相反,我想要可以遍历所有外键的东西。

Can this be done? 能做到吗?

Below is code for an sp that I've used in the past to perform this task (please excuse the indenting): 以下是我过去用来执行此任务的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

This will select a value of 1 if a row has any foreign key references. 如果行中有任何外键引用,则它将选择值为1。

The call you would need would be: 您需要拨打的电话是:

exec usp_ForeignKeyCheck('User','UserID',23)

There is no clean way to iterate through all FK columns where multiple exist. 没有干净的方法可以遍历存在多个FK的所有FK列。 You'd have to build some dynamic SQL to query the system tables and test each in turn. 您必须构建一些动态SQL才能查询系统表并依次测试每个表。

Personally, I wouldn't do this. 就个人而言,我不会这样做。 I know what FKs I have: I'll test each in turn 我知道我拥有哪些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)
...

You wouldn't dynamically iterate through each property of some user object and generically test each one would you? 您不会动态地迭代某个用户对象的每个属性,并且不会对每个对象进行通用测试吗? You'd have code for each property 您将拥有每个属性的代码

This code will give you a list of the foreign keys which are defined for a specifit table: 此代码将为您提供为特殊表定义的外键的列表:

select distinct name from sys.objects where object_id in ( select constraint_object_id from sys.foreign_key_columns as fk 从sys.objects中选择唯一的名称,其中object_id在(从sys.foreign_key_columns中选择constraint_object_id为fk
where fk.Parent_object_id = (select object_id from sys.tables where name = 'tablename') ) 其中fk.Parent_object_id =(从sys.tables中选择object_id,其中name ='tablename')

You can use transaction to check it. 您可以使用事务检查它。 I know it seems like stone ax, but it working fast and stable. 我知道它看起来像石斧,但它可以快速稳定地工作。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM