SQL Server: как узнать, ссылается ли какая-либо строка на строку для удаления
Вы не можете удалить строку, если какая-либо строка ссылается на строку, которую нужно удалить с помощью FK.
Можно ли узнать, ссылается ли какая-либо строка на удаляемую строку перед выполнением оператора DELETE?
Ответы
Ответ 1
Этот script отобразит все таблицы, содержащие строки, которые ссылаются на строку, которую вы пытаетесь удалить:
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)
Предполагается, что внешний ключ не является составным.
Ответ 2
Вариант 1 (обнаружение):
Вы выполняете Select Statement
, чтобы определить, ссылаются ли записи на запись, подлежащую записи, и, если хотите, вручную удалите те записи, которые ссылаются на нее. Это также можно выполнить с помощью триггера, хотя я рекомендую использовать триггеры, потому что они, как правило, удивляют людей (и вас самих) в будущем.
Вариант 2 (Автоматизация):
Вы можете посмотреть Cascading Deletes, который, если он настроен правильно, приведет к тому, что все записи, ссылающиеся на запись, будут удалены, чтобы также быть удалена.
Когда использовать Cascading Deletes (перефразировано из текста, написанного Joel Coehoorn)
- Cascade Delete может иметь смысл, когда семантика отношения может включать описание "является частью". Пример: веб-заказ, позиции веб-заказа.
- Вы не должны использовать Cascade Delete, если вы сохраняете историю или используете мягкое удаление, где вы устанавливаете только столбец удаленных бит.
- Каскадирование может вызвать у вас неприятности, если вы неправильно настроили свои внешние ключи.
- Нецелесообразно использовать каскадирование, прежде чем вы его полностью поймете. Однако это полезная функция и поэтому стоит потратить время на понимание.
Здесь большое обсуждение Cascading Deletes в stackoverflow.
Ответ 3
никто не упомянул об этом, но только для записи я использую много процедур
sp_helpconstraint 'dbo.mytable'
чтобы найти все ограничения, связанные с dbo.mytable, и какие таблицы ссылаются на dbo.mytable.
Я считаю, что это очень полезно и удобно.
Ответ 4
Я улучшил решение Alex Aza.
Я использую softdelete, поэтому необходимо добавить столбец "delete" в состояние "where".
И еще я сделал функцию в TSQL, которая обнаруживает, когда таблица представляет объект, унаследованный в NHibernate, а PK - это FK из родительской таблицы.
Следовать:
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;