Kei*_*ein 4 trigger database-design sql-server referential-integrity cascade
上图是我的数据结构图。它代表一个层次结构,可以包含三种不同类型的“元素”:“A”、“B”和“C”。的关系表明删除级联行为我也喜欢用,如果它是可能的。
所有类型都有共同的属性,包括显示层次结构(父级和索引)中的位置和元素类型的列。这些公共列存储在ElementBase表中。
每种类型的元素还具有独特的属性,这些属性根据元素类型存储在相应的表中。
中的每一行AData,BData并CData引用 中的唯一主行ElementBase。“A”和“C”元素也各自引用“B”元素。“B”元素可以有0个或多个“S”。
我的问题是:如何维护引用完整性并支持级联删除之类的功能?
我希望能够从中删除一行ElementBase并在其中包含相应的行AData,BData或者CData也可以删除。例如,如果从 中删除了“B”类型的元素ElementBase,则首先BData应删除相应的行,然后需要在表ElementBase和CData表中删除所有引用它的“C”类型元素,并且所有“A” "-type 元素需要将它们的引用设置为NULLin AData。
最重要的是:如果我删除的元素有任何类型的子元素,我希望相同的逻辑在层次结构中递归运行。
由于ElementBase是自引用,我无法使用该ON DELETE CASCADE表中的简单功能。我也不能使用它,AData或者CData因为它们都引用了BData这可能会导致“多个级联路径”,这在 SQL Server 中显然是邪恶的。
我发现的另一种选择是INSTEAD OF触发器。问题是这种行为必须是递归的,我无法弄清楚如何使它们能够递归并最终在最后进行原始删除。
我想我已经在这个基本设计中捕捉到了你需要的东西:
层次结构的自我fk:
CREATE TABLE dbo.ElementBase
(
id integer NOT NULL,
parent_id integer NOT NULL,
element_type char(1) NOT NULL,
-- id key
CONSTRAINT [PK dbo.ElementBase id]
PRIMARY KEY CLUSTERED (id),
-- fk target
CONSTRAINT [UQ dbo.ElementBase id, element_type]
UNIQUE NONCLUSTERED (id, element_type),
-- self fk
CONSTRAINT [FK dbo.ElementBase parent_id id]
FOREIGN KEY (parent_id)
REFERENCES dbo.ElementBase (id),
-- valid element types
CONSTRAINT [CK dbo.ElementBase element_type]
CHECK (element_type IN ('a', 'b', 'c')),
-- for maintenance
INDEX [IX dbo.ElementBase parent_id]
NONCLUSTERED (parent_id)
);
Run Code Online (Sandbox Code Playgroud)
级联删除自ElementBase:
CREATE TABLE dbo.BData
(
id integer NOT NULL,
element_type AS CONVERT(char(1), 'b') PERSISTED,
-- id key
CONSTRAINT [PK dbo.BData id]
PRIMARY KEY CLUSTERED (id),
-- fk to ElementBase
CONSTRAINT [FK Bdata ElementBase id, element_type]
FOREIGN KEY (id, element_type)
REFERENCES dbo.ElementBase (id, element_type)
ON DELETE CASCADE
);
Run Code Online (Sandbox Code Playgroud)
没有级联删除ElementBase; SET NULL级联删除自BData:
CREATE TABLE dbo.AData
(
id integer NOT NULL,
element_type AS CONVERT(char(1), 'a') PERSISTED,
b_element integer NULL,
-- id key
CONSTRAINT [PK dbo.AData id]
PRIMARY KEY CLUSTERED (id),
-- fk to ElementBase
CONSTRAINT [FK Adata ElementBase id, element_type]
FOREIGN KEY (id, element_type)
REFERENCES dbo.ElementBase (id, element_type)
ON DELETE NO ACTION,
-- fk to BData
CONSTRAINT [FK dbo.AData dbo.BData id b_element]
FOREIGN KEY (b_element)
REFERENCES dbo.BData (id)
ON DELETE SET NULL,
-- fk lookup
INDEX [IDX dbo.AData b_element]
NONCLUSTERED (b_element),
);
Run Code Online (Sandbox Code Playgroud)
没有级联删除ElementBase; SET NULL级联删除自BData:
CREATE TABLE dbo.CData
(
id integer NOT NULL,
element_type AS CONVERT(char(1), 'c') PERSISTED,
b_element integer NOT NULL,
-- id key
CONSTRAINT [PK dbo.CData id]
PRIMARY KEY CLUSTERED (id),
-- fk to ElementBase
CONSTRAINT [FK Cdata ElementBase]
FOREIGN KEY (id, element_type)
REFERENCES dbo.ElementBase (id, element_type)
ON DELETE NO ACTION,
-- fk to BData
CONSTRAINT [FK dbo.CData dbo.BData b_element id]
FOREIGN KEY (b_element)
REFERENCES dbo.BData (id)
ON DELETE CASCADE,
-- fk lookup
INDEX [IDX dbo.CData b_element]
NONCLUSTERED (b_element),
);
Run Code Online (Sandbox Code Playgroud)
级联删除自BData:
CREATE TABLE dbo.S
(
s_id integer NOT NULL,
b_element integer NOT NULL,
-- id key
CONSTRAINT [PK dbo.S s_id]
PRIMARY KEY CLUSTERED (s_id),
-- fk to BData
CONSTRAINT [FK dbo.S dbo.BData b_element id]
FOREIGN KEY (b_element)
REFERENCES dbo.BData (id)
ON DELETE CASCADE,
-- fk lookup
INDEX [IDX dbo.S b_element]
NONCLUSTERED (b_element),
);
Run Code Online (Sandbox Code Playgroud)
这将处理删除 中的相关项目ElementBase,然后将删除级联到AData和CData。级联删除操作BData和S由RI处理:
CREATE OR ALTER TRIGGER [dbo.ElementBase IOD Cascade]
ON dbo.ElementBase
INSTEAD OF DELETE AS
BEGIN
SET ROWCOUNT 0;
SET NOCOUNT ON;
-- Exit if no work to do
IF NOT EXISTS (SELECT * FROM Deleted) RETURN;
-- Holds ElementBase rows identified for deletion
CREATE TABLE #ToDelete
(
id integer PRIMARY KEY,
element_type char(1) NOT NULL
);
-- Find all related ElementBase records
WITH R AS
(
-- Anchor: parent ElementBase rows
SELECT D.id, D.element_type
FROM Deleted AS D
UNION ALL
-- Recursive: children
SELECT EB.id, EB.element_type
FROM R
JOIN dbo.ElementBase AS EB
ON EB.parent_id = R.id
AND EB.id <> R.id
)
INSERT #ToDelete
(id, element_type)
SELECT DISTINCT
R.id,
R.element_type
FROM R
OPTION (MAXRECURSION 0);
-- Delete related CData records (manual cascade)
DELETE CD
FROM #ToDelete AS TD
JOIN dbo.CData AS CD
ON CD.id = TD.id
WHERE
TD.element_type = 'c';
-- Delete related AData records (manual cascade)
DELETE AD
FROM #ToDelete AS TD
JOIN dbo.AData AS AD
ON AD.id = TD.id
WHERE
TD.element_type = 'a';
-- Delete ElementBase (BData, S records via cascade)
DELETE EB
FROM #ToDelete AS TD
JOIN dbo.ElementBase AS EB
ON EB.id = TD.id;
END;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
870 次 |
| 最近记录: |