SQL Server: как узнать, ссылается ли какая-либо строка на строку для удаления - PullRequest
15 голосов
/ 10 июня 2011

Вы не можете удалить строку, если какая-либо строка ссылается на строку для удаления через FK.

Можно ли узнать, ссылается ли какая-либо строка на строку, подлежащую удалению, перед выполнением инструкции DELETE?

Ответы [ 4 ]

20 голосов
/ 10 июня 2011

Этот скрипт покажет все таблицы, в которых есть строки, которые ссылаются на строку, которую вы пытаетесь удалить:

declare @RowId int = 1
declare @TableName sysname = 'ParentTable'

declare @Command varchar(max) 

select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) + 
    ''' where exists(select * from ' + object_name(parent_object_id) + ' where ' + col.name+ ' = ' + cast(@RowId as varchar) + ')' 
from sys.foreign_key_columns fkc
    join sys.columns col on
        fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
where object_name(referenced_object_id) = @TableName

execute (@Command)

Предположение, что внешний ключ не является составным.

3 голосов
/ 10 июня 2011

Вариант 1 (Обнаружение) :

Вы выполняете Select Statement, чтобы определить, ссылаются ли какие-либо записи на запись, подлежащую удалению, - и , еслиВы хотите , вручную удалить те записи, которые ссылаются на него.Это также может быть достигнуто с помощью триггера, хотя я рекомендую против триггеров, потому что они имеют тенденцию удивлять людей (и вас самих) в будущем.

Опция 2 (Автоматизация) :

Можно просмотреть Каскадное удаление , которое, если настроено правильно, приведет к тому, что все записи, ссылающиеся на Запись, будутбыть удаленным, чтобы также быть удаленным.

Когда использовать каскадное удаление (Перефразировано из текста, написанного Джоэл Коухорн )

  • Каскадное удаление может иметь смысл, когда семантикаотношения могут включать в себя описание «является частью».Пример: веб-заказ, позиции веб-заказа
  • Вы не должны использовать Каскадное удаление, если вы сохраняете историю или используете мягкое удаление, когда вы устанавливаете только удаленный битовый столбец
  • Каскадирование может привести кпроблемы, если вы неправильно настроили свои внешние ключи.
  • Не стоит использовать каскадирование, прежде чем вы его полностью разберетесь.Тем не менее, это полезная функция, и поэтому стоит потратить время на ее понимание.

Вот отличное обсуждение Каскадное удаление на стеке потока.

0 голосов
/ 04 июля 2014

Я улучшил решение Алекса Азы.

Я использую softdelete, поэтому необходимо добавить столбец «delete» в условии «где».И еще я сделал функцию в TSQL, которую он обнаруживает, когда таблица представляет объект, унаследованный в NHibernate, а PK является FK из родительской таблицы.

Follow:

declare @RowId int = 4
declare @TableName sysname = 'TABLE'

declare @Command varchar(max) 

select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) + 
    ''' where exists(select * from ' + object_name(parent_object_id) + 
    CASE
        WHEN EXISTS(select object_name(object_id) from sys.columns col where name = 'deleted' and object_id = parent_object_id) 
            THEN ' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 '
        when dbo.ParentIdFromTable(object_name(parent_object_id)) <> ''
            then ' inner join ' + dbo.ParentIdFromTable(object_name(parent_object_id)) + ' on id = ' + dbo.PrimaryKey(object_name(parent_object_id))
                +' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 '
        else 
            ' where ' + col.name+ ' = ' + cast(@RowId as varchar) 
      END
    + ')' 
from sys.foreign_key_columns fkc
    join sys.columns col on
        fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
where object_name(referenced_object_id) = @TableName

PRINT @Command
execute (@Command)

Функции зависимостей:

CREATE FUNCTION dbo.ParentIdFromTable(@Table varchar(255))
RETURNS varchar(255) 
AS 
BEGIN
    declare @tableParent varchar(255) = ''

    if exists(select pk.TABLE_NAME, pk.COLUMN_NAME, col.name, object_name(referenced_object_id) Referenced, object_name(parent_object_id) as Parent
        from sys.columns col
            inner join sys.foreign_key_columns fkc on
                fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
            inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
                pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
        WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
        AND table_name = @table)
    begin

        while exists(select *
            from sys.columns col
            inner join sys.foreign_key_columns fkc on
                fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
            inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
                pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
            WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 AND table_name = @table)
        begin
            -- Descobrir o parent, column
            select  @tableParent = object_name(referenced_object_id)
            from sys.columns col
            inner join sys.foreign_key_columns fkc on
                fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
            inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
                pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
            WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
            AND table_name = @table

            --print @tableParent
            set @table = @tableParent
        end
    end

    return @tableParent;
END;
GO


CREATE FUNCTION dbo.PrimaryKey(@Table varchar(255))
RETURNS varchar(255) 
AS 
BEGIN
    declare @columnName varchar(255) = ''
    -- Descobrir o parent, column
    select @columnName = COLUMN_NAME
    from INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
    WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
    AND table_name = @Table

    return @columnName
end;
0 голосов
/ 30 апреля 2014

никто не упомянул об этом, но просто для записи я часто использую процедуру

sp_helpconstraint 'dbo.mytable'

, чтобы найти все ограничения, связанные с dbo.mytable, и какие таблицы ссылаются на dbo.mytable. Я нахожу это очень полезным и удобным.

...