Tahir Hassan
Tahir Hassan

Reputation: 5817

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:

  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".

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? Is there any SQL code which I could run which will check all foreign keys of a table if that row is being referenced?

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. Instead I want something that will traverse all foreign keys.

Can this be done?

Upvotes: 1

Views: 2079

Answers (4)

Viacheslav Smityukh
Viacheslav Smityukh

Reputation: 5833

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

Upvotes: 0

MaryamAyd
MaryamAyd

Reputation: 151

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
where fk.Parent_object_id = (select object_id from sys.tables where name = 'tablename') )

Upvotes: 0

Jon Egerton
Jon Egerton

Reputation: 41549

Below is code for an sp that I've used in the past to perform this task (please excuse the indenting):

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.

The call you would need would be:

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

Upvotes: 2

gbn
gbn

Reputation: 432260

There is no clean way to iterate through all FK columns where multiple exist. You'd have to build some dynamic SQL to query the system tables and test each in turn.

Personally, I wouldn't do this. I know what FKs I have: I'll test each in turn

...
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

Upvotes: 0

Related Questions