我有这个概念代码(带有一些注释):
IF EXISTS (SELECT * FROM information_schema.schemata WHERE schema_name = 'test')
SET NOEXEC ON
GO
-- didn't use EXEC sp_executesql for Syntax Highlighting and Code Compeltion in MSSMS
CREATE SCHEMA [test]
GO
SET NOEXEC OFF
GO
BEGIN TRANSACTION
-- child is a list
CREATE TABLE [test].[Child] (
[ChildId] BIGINT NOT NULL,
CONSTRAINT [PK_Child] PRIMARY KEY CLUSTERED ([ChildId]) WITH (IGNORE_DUP_KEY = ON)
)
-- parent is a tree
CREATE TABLE [test].[Parent] (
[ParentId] BIGINT NOT NULL,
[ParentPid] BIGINT DEFAULT NULL,
FOREIGN KEY([ParentPid]) REFERENCES [test].[Parent] ([ParentId]),
CONSTRAINT [PK_Parent] PRIMARY KEY CLUSTERED ([ParentId]) WITH (IGNORE_DUP_KEY = ON)
)
-- each child can have any number of parents
CREATE TABLE [test].[ChildParent] (
[ChildId] BIGINT NOT NULL,
FOREIGN KEY([ChildId]) REFERENCES [test].[Child] ([ChildId]) ON DELETE CASCADE,
[ParentId] BIGINT NOT NULL,
FOREIGN KEY([ParentId]) REFERENCES [test].[Parent] ([ParentId]) ON DELETE CASCADE,
CONSTRAINT [PK_ChildParent] PRIMARY KEY CLUSTERED ([ChildId], [ParentId]) WITH (IGNORE_DUP_KEY = ON)
)
-- () This table is internal and used readonly, generated by a trigger on ChildParent
-- that creates associations between each Child & Parent + all the Parent's Parents
-- this way flatteting the Parent structure and making deep querying easier.
-- () ChildId + ParentPid point directly to the original Child + Parent relationship
-- that when it's deleted, it automatically deletes all the Child + Parent's Parents.
-- () I also need the table to automagically delete any row when any Child/Parent's gone.
CREATE TABLE [test].[Parent_Child] (
[ParentId] BIGINT NOT NULL,
FOREIGN KEY([ParentId]) REFERENCES [test].[Parent] ([ParentId]) ON DELETE CASCADE,
[ChildId] BIGINT NOT NULL,
FOREIGN KEY([ChildId]) REFERENCES [test].[Child] ([ChildId]) ON DELETE CASCADE,
[ParentPid] BIGINT NOT NULL,
FOREIGN KEY([ParentPid]) REFERENCES [test].[Parent] ([ParentId]) ON DELETE CASCADE,
FOREIGN KEY([ChildId], [ParentPid]) REFERENCES [test].[ChildParent] ([ChildId], [ParentId]) ON DELETE CASCADE,
CONSTRAINT [PK_Parent_Child] PRIMARY KEY CLUSTERED ([ParentId], [ChildId], [ParentPid]) WITH (IGNORE_DUP_KEY = ON)
)
COMMIT TRANSACTION
最后一张表不会创建并且I know why ,即使我根本不同意它,因为我看不出这个确切的场景如何破坏任何东西或使 SQLServer 变得困难。 MySQL (或者至少在 1 年前我上次使用它时) 没有问题。
我也知道我在上面(非法 SQL) 中所做的任何更改(删除 DELETE CASCADE,一起删除 FOREIGN KEYS) 将迫使我对客户端 (C#)
破坏了我精心设计的数据库。
Considering I'd like to keep all
Parent_Child
maintenance SQLServer-side so the C# client just reads it, is there any workaround for this situation?
PS: 考虑使用 INSTEAD OF DELETE
触发器,但文档说您不能在具有 FOREIGN KEY DELETE CASCADE
。
错误: 可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。。
最佳答案
我设法解决了这种情况,但没有在客户端做任何工作:
- 创建了一个新表,我在其中使用
Parent
上的触发器展平了整个父级层次结构。 - 使用这个新表进行查询只是与我的原始设计不同的连接,而不是递归的。
表结构:
CREATE TABLE [test].[ParentTree]
(
[ParentId] BIGINT NOT NULL, -- no fkey, but I double the value on column 2 so no need
[ParentPid] BIGINT NOT NULL,
FOREIGN KEY([ParentPid]) REFERENCES [dbo].[Parent] ([ParentId]) ON DELETE CASCADE,
CONSTRAINT [PK_ParentTree] PRIMARY KEY CLUSTERED ([ParentId], [ParentPid]) WITH (IGNORE_DUP_KEY = ON)
)
GO
因此,扁平层次结构是通过 Parent
上的触发器、递归 CTE
函数和一个 OUTER APPLY
来实现的,以针对 parent
。如果我有 'x'
的 'y'
子级的 'z'
子级,那么我的平面表包含:
[Child] [Parent]
x x -- notice child is parent of child
y y -- notice child is parent of child
y x
z z -- notice child is parent of child
z y
z x
这让我可以对平面数据集进行深度查询。我还可以在第 2 列上使用 DELETE CASCADE
,因为我将子值加倍,这样我就可以避免没有双重 fkeys。
查询[Parent]
中的所有[Child]
X
(包括child Y
和 X
的 Z
) 我只是这样做:
DECLARE @ParentId BIGINT = 0
SELECT DISTINCT [ChildId]
FROM [test].[ChildParent] [t1]
JOIN [test].[ParentTree] [t2]
ON ([t2].[ParentId] = [t1].[ParentId] AND [t2].[ParentPid] = @ParentId)
GO
我什至可以进一步降低复杂性,但这是我针对 fkey 级联困惑的解决方法。
PS:复制一个 fkey(这样我就可以只有一个)可能也适用于原始设计,但我接受这个解决方案,所以我不会尝试更改原来的代码是这样工作的。
关于sql-server - SQLServer 两个外键指向同一列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31937876/