我有以下表格
parent
和child1
.
ParentID1
表中的
ParentID2
和child1
指向ParentID
中的parent
。
架构:
-- Table creation logic
--parent table
CREATE TABLE [dbo].[Parent]
(
[ParentID] [bigint] NOT NULL,
[Data] [varchar](10) NOT NULL,
CONSTRAINT [PK_Parent]
PRIMARY KEY CLUSTERED ([ParentID] ASC)
)
GO
-- Child table
CREATE TABLE [dbo].[Child1]
(
[Child1ID] [bigint] NOT NULL,
[ParentID1] [bigint] NULL,
[ParentID2] [bigint] NULL,
[Data] [varchar](10) NULL,
CONSTRAINT [PK_Child1]
PRIMARY KEY CLUSTERED ([Child1ID] ASC)
)
GO
-- foreign key constraints
ALTER TABLE [dbo].[Child1] WITH CHECK
ADD CONSTRAINT [FK_Child1_ParentID1]
FOREIGN KEY ([ParentID1]) REFERENCES [dbo].[Parent] ([ParentID])
GO
ALTER TABLE [dbo].[child1] WITH CHECK
ADD CONSTRAINT [FK_Child1_ParentID2]
FOREIGN KEY ([ParentID2]) REFERENCES [dbo].[Parent] ([ParentID])
GO
-- inserting data into the tables
INSERT INTO Parent VALUES (1, 'test')
INSERT INTO Parent VALUES (2, 'test')
INSERT INTO Parent VALUES (3, 'test')
INSERT INTO Parent VALUES (4, 'test')
INSERT INTO Parent VALUES (5, 'test')
INSERT INTO Parent VALUES (6, 'test')
INSERT INTO Parent VALUES (7, 'test')
INSERT INTO Parent VALUES (8, 'test')
INSERT INTO Child1 VALUES (1, 1, 2, 'test')
INSERT INTO Child1 VALUES (2, 2, 3, 'test')
INSERT INTO Child1 VALUES (3, 3, 4, 'test')
INSERT INTO Child1 VALUES (4, 4, 5, 'test')
INSERT INTO Child1 VALUES (5, 5, 6, 'test')
INSERT INTO Child1 VALUES (6, 6, 7, 'test')
GO
每当我从父母那里删除一条记录时,我也想从孩子那里删除相应的记录。我尝试了“删除级联”但出现错误 - “可能导致循环或多个级联路径”。
所以,我开始创建触发器来处理这个问题。由于我有一个大数据库,我想在表上动态创建触发器。 因此,我循环访问以下元数据表 (LF_DB_Relations),其中包含 parenttable、childtable、primarykeycolumn 和 foreignkeycolumn 以动态创建触发器:
由于同一个父项和子项有多个父子条目,当它试图在同一个表上创建第二个“INSTEAD OF DELETE”触发器时创建触发器失败。
这是我用来动态创建触发器的查询:
DECLARE @childtable varchar(50);
DECLARE @fkcolumn varchar(50);
DECLARE @parenttable varchar(50);
DECLARE @pkcolumn varchar(50);
DECLARE @RowCnt BIGINT = 0;
DECLARE @count INT;
SET @count = 1;
-- get a count of total rows to process
SELECT @RowCnt = COUNT(0) FROM dbo.LF_DB_Relations;
WHILE @count<= @RowCnt
BEGIN
select
@childtable = childtable,
@fkcolumn = fkcolumn,
@parenttable = parenttable,
@pkcolumn = pkcolumn
FROM dbo.LF_DB_Relations
WHERE ID = @count
EXEC('CREATE TRIGGER '+ 'Delete_' + @parenttable + '_Purging_' +@count+
' ON ' + @parenttable +
' INSTEAD OF DELETE ' +
'AS ' +
'BEGIN ' +
'SET NOCOUNT ON; '+
'DELETE FROM ' + @childtable +' WHERE ' + @fkcolumn +' IN (SELECT ' + @pkcolumn + ' FROM DELETED) ' +
' DELETE FROM ' + @parenttable + ' WHERE ' + @pkcolumn + ' IN (SELECT ' + @pkcolumn +' FROM DELETED) ' +
'END');
PRINT @childtable;
SET @count = @count + 1;
END;
我的动态触发查询有什么问题?我如何在每个父表上创建单个触发器,并删除所有子表的所有 FK 的查询?
您多次引用
parent
表,因为在 child
中您有多个外键。因此,您的代码正在创建(或至少试图创建)多个 INSTEAD OF DELETE
触发器,这是不允许的。
假设数据看起来像:
并且您只需要为
Categories
表创建一个触发器。
所以你需要像这样对数据进行分组:
DROP TABLE IF EXISTS #LF_DB_Relations;
CREATE TABLE #LF_DB_Relations (
childtable NVARCHAR(255),
fkcolumn NVARCHAR(255),
parenttable NVARCHAR(255),
pkcolumn NVARCHAR(255)
);
INSERT INTO #LF_DB_Relations (childtable, fkcolumn, parenttable, pkcolumn)
VALUES ('OrderDetails', 'OrderID', 'Orders', 'OrderID'),
('OrderDetails', 'ProductID', 'Products', 'ProductID'),
('Products', 'CategoryID', 'Categories', 'CategoryID'),
('Products', 'SupplierID', 'Categories', 'CategoryID');
SELECT * FROM #LF_DB_Relations;
DECLARE @childtable varchar(50);
DECLARE @fkcolumn varchar(50);
DECLARE @parenttable varchar(50);
DECLARE @pkcolumn varchar(50);
WHILE EXISTS (SELECT 1 FROM #LF_DB_Relations)
BEGIN
SELECT TOP 1 @parenttable = parenttable
FROM #LF_DB_Relations;
SELECT 'CREATE TRIGGER '+ 'Delete_' + MAX(parenttable) + '_Purging' + CHAR(10) +
' ON ' + MAX(parenttable) + CHAR(10) +
' INSTEAD OF DELETE ' + CHAR(10) +
'AS ' + CHAR(10) +
'BEGIN ' + CHAR(10) +
'SET NOCOUNT ON; '+ CHAR(10) +
STRING_AGG(CAST('DELETE FROM ' + childtable + ' WHERE ' + fkcolumn + ' IN (SELECT ' + pkcolumn + ' FROM DELETED);' AS NVARCHAR(MAX)), CHAR(10)) +
STRING_AGG(CAST('DELETE FROM ' + parenttable + ' WHERE ' + pkcolumn + ' IN (SELECT ' + pkcolumn + ' FROM DELETED);' AS NVARCHAR(MAX)), CHAR(10)) +
'END'
FROM #LF_DB_Relations
WHERE parenttable = @parenttable;
DELETE FROM #LF_DB_Relations
WHERE parenttable = @parenttable;
END;
您需要处理每个父表一次,并在其触发器中删除所有
child
行。
请注意,以上代码尚未准备好用于生产。你需要:
STRING_AGG
用于父表DELETE
语句(例如,如果父表中的多个列用作子表的外键)