sql-server - SQLServer 两个外键指向同一列

标签 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#) 破坏了我精心设计的数据库。

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 YXZ) 我只是这样做:

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/

相关文章:

sql-server - 在 MSSQL 中使用 OVER PARTITION 时 SUM 不一致

sql - 更新大型表的表架构的最佳方法(SQL Server)

sql-server - 如何在 SQL Server 2005 Express 中启用全文索引?

c# - MVC4 网站仅适用于一台 PC,不适用于其他 PC,失败并显示 "SQL Network Interfaces, error: 26"

sql - 使用以前的非空值填充表中的空值。每列有几个空值?

sql-server - EF Core 3.1 查询中 HasConversion 和 HasMaxLength 映射的不一致行为

sql-server - dapper-extensions GetList() 与 (nolock)

sql-server - 使用powershell将txt中的行变成列

sql-server - 如果我在 TRY 中打开一个 Cursor 那么我应该在哪里关闭它?

mysql - 按集合中最多标签排序