前言:
我想在SQL Server(2016 - v13.0)中创建一个存储过程,以手动跨具有外键约束的表(包括自引用表)执行级联删除。由于使用时态表,我需要自定义实现,该时态表不支持级联删除。我发现这个限制非常令人沮丧,特别是考虑到临时表是为了跟踪删除而设计的。为了删除一行,必须首先删除依赖于它的所有行。
我应该清楚:我不想触摸历史表中的数据,我只关心删除当前表中的行。
我对 SQL 的了解不是很深,我不确定这是否是最好的方法。我考虑过使用软删除,但我不喜欢它们,因为它们会带来混乱并且需要勤奋地维护数据准确性。我听说其他人建议使用存储过程来显式执行删除,但在我的场景中,我有太多表,无法为每个表单独实现删除过程;相反,我需要一个适用于任何给定表行的动态表。
目标:
存储过程应该:
@SchemaName
(架构名称)、@TableName
(表名称)和@Id
(要删除的实体的主键)。示例场景:
考虑一下我正在使用的数据库的超简化表示。
期望:
鉴于此层次结构,如果我尝试删除
Folder
,该过程应递归删除所有依赖的 ElementInstances
、Elements
、Pages
、Forms
和子 Folders
(按顺序)。
问题:
任何指导或示例将不胜感激!
我决定为每个表编写一个删除程序。下面是我正在构建的应用程序的一个示例,与我的 OP 中给出的示例不是 1:1,但它遵循相同的原则。
以下是删除具有自引用的顶级实体的过程。它将首先找到其所有子文件夹并对其执行
DeleteFolder
过程以开始某种递归过程,然后它将通过 DeleteForm
过程删除属于它们的表单。
DeleteFolder.sql
CREATE PROCEDURE [dbo].[DeleteFolder]
@FolderId INT
AS
BEGIN
SET NOCOUNT ON;
-- Delete child folders
DECLARE @ChildFolderId INT;
DECLARE ChildFoldersCursor CURSOR FOR
SELECT [Id] FROM [ui].[Folders] WHERE [ParentId] = @FolderId;
OPEN ChildFoldersCursor;
FETCH NEXT FROM ChildFoldersCursor INTO @ChildFolderId;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [dbo].[DeleteFolder] @ChildFolderId;
FETCH NEXT FROM ChildFoldersCursor INTO @ChildFolderId;
END
CLOSE ChildFoldersCursor;
DEALLOCATE ChildFoldersCursor;
-- Delete related forms
DECLARE @FormId INT;
DECLARE FormsCursor CURSOR FOR
SELECT [Id] FROM [config].[Forms] WHERE [FolderId] = @FolderId;
OPEN FormsCursor;
FETCH NEXT FROM FormsCursor INTO @FormId;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [dbo].[DeleteForm] @FormId;
FETCH NEXT FROM FormsCursor INTO @FormId;
END
CLOSE FormsCursor;
DEALLOCATE FormsCursor;
-- Delete the folder
DELETE FROM [ui].[Folders] WHERE [Id] = @FolderId;
END
这是上一个中调用的过程。它详细说明了如何删除依赖于给定表单的实体。同样,它继续使用存储过程进行删除,而不是显式行删除。
DeleteForm.sql
CREATE PROCEDURE [dbo].[DeleteForm]
@FormId INT
AS
BEGIN
SET NOCOUNT ON;
-- Delete related pages
DECLARE @PageId INT;
DECLARE PagesCursor CURSOR FOR
SELECT [Id] FROM [ui].[Pages] WHERE [FormId] = @FormId;
OPEN PagesCursor;
FETCH NEXT FROM PagesCursor INTO @PageId;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [dbo].[DeletePage] @PageId;
FETCH NEXT FROM PagesCursor INTO @PageId;
END
CLOSE PagesCursor;
DEALLOCATE PagesCursor;
-- Delete related lookup lists
DECLARE @LookupListId INT;
DECLARE LookupListsCursor CURSOR FOR
SELECT [Id] FROM [config].[LookupLists] WHERE [FormId] = @FormId;
OPEN LookupListsCursor;
FETCH NEXT FROM LookupListsCursor INTO @LookupListId;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [dbo].[DeleteLookupList] @LookupListId;
FETCH NEXT FROM LookupListsCursor INTO @LookupListId;
END
CLOSE LookupListsCursor;
DEALLOCATE LookupListsCursor;
-- Delete the related Elements
DECLARE @ElementId INT;
DECLARE ElementsCursor CURSOR FOR
SELECT [Id] FROM [config].[Elements] WHERE [FormId] = @FormId;
OPEN ElementsCursor;
FETCH NEXT FROM ElementsCursor INTO @ElementId;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [dbo].[DeleteElement] @ElementId;
FETCH NEXT FROM ElementsCursor INTO @ElementId;
END
CLOSE ElementsCursor;
DEALLOCATE ElementsCursor;
-- Delete the form
DELETE FROM [config].[Forms] WHERE [Id] = @FormId;
END
最终,我们会删除不依赖于任何东西的叶子,因此我们模拟了
ON DELETE CASCADE
,具有更多的灵活性和(不幸的是)更多的代码。