让我们看看着名的Nortwind数据库。假设我运行DELETE FROM Clients
。
在MSAccess中,当针对具有参照完整性约束的表运行DELETE语句时,Jet将删除可以删除的记录,并保留其他记录。在这种情况下,它只会删除没有Clients
的{{1}}
在SQL Server中,执行此操作似乎只是失败,并显示一条消息,指出Orders
。
我的问题是:是否有一种简单的方法让SQL Server只删除那些可以删除的记录?或者我是否必须添加The DELETE statement conflicted with the REFERENCE constraint ....
?
换句话说,我可以让SQL Server WHERE ClientId NOT IN (SELECT Id FROM Clients)
像Jet的DELETE
一样工作吗?
有关信息:我不是很懒,但有很多限制,我想保持我的代码简单......
答案 0 :(得分:1)
您的选择是:
答案 1 :(得分:1)
如果你想留下有FK引用的行,那么只有几个选项,而且它们都不漂亮:
'最糟糕'选项实际上取决于有多少个FK,你要删除多少行以及一行有FK依赖性的可能性。如果这是一个相对罕见的事件,那么选项#3可能是最好的,尽管我倾向于倾向于前两个选项。
答案 2 :(得分:1)
另一种方法是使用CURSOR
和TRY .. CATCH
块来循环删除(逐行)以忽略删除引用行的问题。
在这种方法中,您不必为现有和未来的约束建模。
示例:
SET NOCOUNT ON; -- use not to have "(N row(s) affected)" for each deleted row
DECLARE del_cursor CURSOR
FOR SELECT ClientID FROM Clients
DECLARE @CurrentClientID INT -- use your proper type
DECLARE @message VARCHAR(200) -- just for building messages
OPEN del_cursor
FETCH NEXT FROM del_cursor
INTO @CurrentClientID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
DELETE FROM Clients WHERE CURRENT OF del_cursor
END TRY
BEGIN CATCH
SET @message = 'Row ' + CAST(@CurrentClientID AS VARCHAR) + ' cannot be deleted - skipping.'
PRINT @message
END CATCH
FETCH NEXT FROM del_cursor
INTO @CurrentClientID
END
CLOSE del_cursor
DEALLOCATE del_cursor
您可以使用CREATE PROCEDURE DeleteClients
将上述示例换行并使用EXEC DeleteClients
代替DELETE FROM Clients
答案 3 :(得分:0)
我知道这是一个有点复杂的解决方案,但你只需要做一次。
阻止删除的触发器怎么样?
create table parent(
id int not null primary key
)
create table son(
id int not null primary key,
idparent int)
alter table son add foreign key(idparent) references parent(id)
insert into parent values(1)
insert into parent values(2)
insert into parent values(3)
insert into son values(1,1)
--select * from parent
--select * from son
create trigger preventDelete
on parent
instead of delete
as
begin
delete from parent where id not in (select idparent from son) and id in (select id from deleted)
end
delete from parent
记录2和3将被删除
答案 4 :(得分:0)
试试这个,它会生成你的陈述。请原谅我写得很快的格式:
DECLARE @i INT, @SQL NVARCHAR(2000), @TABLENAME NVARCHAR(100)
SET @i = 1
SET @TABLENAME = 'informer_alert'
SET @SQL = ''
DECLARE @col VARCHAR(50), @basecol VARCHAR(50), @tname VARCHAR(50)
DECLARE @TABLE TABLE ([table] VARCHAR(50), col VARCHAR(50), basecol VARCHAR(50))
INSERT INTO @TABLE
SELECT t.name, sc.name, sc2.name
FROM sys.foreign_key_columns fk
INNER JOIN sys.tables t ON fk.parent_object_id = t.OBJECT_ID
INNER JOIN syscolumns sc ON fk.parent_column_id = sc.colorder
AND sc.id = fk.parent_object_id
INNER JOIN syscolumns sc2 ON fk.referenced_object_id = sc2.id
AND fk.constraint_column_id = sc2.colorder
WHERE fk.referenced_object_id = (SELECT OBJECT_ID
FROM sys.tables
WHERE name = @TABLENAME)
WHILE (@i <= (SELECT COUNT(*) FROM @TABLE))
BEGIN
SELECT @tname = [table], @col = col, @basecol = basecol
FROM (SELECT ROW_NUMBER() OVER(ORDER BY col) [N],
[table], col, basecol
FROM @TABLE) A
WHERE A.N = @i
SET @SQL = @SQL + ' DELETE FROM ' + @TABLENAME + ' WHERE ' + @basecol + ' NOT IN (SELECT ' + @col+ ' FROM ' + @tname + ')'
SET @i = @i + 1
END
SELECT @SQL
答案 5 :(得分:0)
CREATE VIEW
放在桌面上。WHERE
子句以仅获取可删除的行。DELETE FROM ClientsDeletable
。脚本示例:
CREATE VIEW ClientsDeletable
AS
SELECT *
FROM Clients
WHERE
ClientID NOT IN (SELECT CliID FROM ForeignTab1)
AND
ClientID NOT IN (SELECT CliID FROM ForeignTab2)
注意,FROM
不能包含JOIN - 其他方式会出错:
Msg 4405, Level 16, State 1, Line 1
View or function 'ClientsDeletable' is not updatable because the modification affects multiple base tables.