SQLServer两个外键指向同一列

时间:2015-08-11 09:21:30

标签: sql-server

我有这个概念代码(带一些注释)

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#)并打破我设计整齐的数据库。

  

考虑到我希望保留所有Parent_Child维护SQLServer端,以便C#客户端只读取它,是否有针对这种情况的解决方法?

PS: 考虑使用INSTEAD OF DELETE触发器,但doc说你不能在{{1}的表上使用它}。

错误: 可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

1 个答案:

答案 0 :(得分:0)

我设法为这种情况创建了一种解决方法,而没有在客户端做任何工作:

  • 创建了一个新表格,我在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来实现平面层次结构,以对Parents使用该函数。如果我有'z' 'y'孩子'x'的孩子,那么我的公寓桌子包含:

[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。

要查询[Child] [Parent] 中的所有X(包括Y的{​​{1}}和Z {}}中的所有{/ 1}}

X

我甚至可能进一步降低复杂性,但这是我对fkey级联混乱的解决方法。

PS 复制一个fkey(所以我只能拥有一个)可能也适用于原始设计,但我对这个解决方案没问题,所以我不会尝试改变原始代码就像这样工作。