6

嗨,我在这方面有点挣扎,可以使用一些想法......

假设我的数据库有以下表格;客户 供应商 销售发票 采购发票 货币

等等等等

我希望能够将“Notes”记录添加到任何类型的记录中

Notes 表会这样

NoteID        Int (PK)
NoteFK        Int
NoteFKType    Varchar(3)
NoteText      varchar(100)
NoteDate      Datetime

NoteFK 是客户或供应商等的 PK 并且 NoteFKType 表示注释所针对的记录类型

现在我意识到我不能添加一个引用多个表的 FK,而不需要在所有表中都存在 NoteFK。

那么您将如何设计上述内容?注释 FK 需要在上述任何表格中

干杯,丹尼尔

4

8 回答 8

3

你必须接受你不能教数据库关于这个外键约束的限制。所以你将不得不做没有完整性检查(和级联删除)。

你的设计很好。它很容易扩展到额外的表,每个实体可以有多个注释,目标表甚至不需要知道注释功能。

这种设计相对于每个实体表使用单独的笔记表的一个优势是,您可以轻松地对所有笔记运行查询,例如“最近的笔记”或“由给定用户创建的所有笔记”。

至于该表太大的论点,将其拆分为五个表会将表缩小到其大小的五分之一左右,但这对基于索引的访问没有任何影响。数据库是为处理大表而构建的(只要它们被正确索引)。

于 2009-07-24T11:43:15.590 回答
2

我认为你的设计是好的,如果你能接受这个事实,数据库系统不会检查一个笔记是否引用了其他表中的现有实体。这是我能想到的唯一一种不需要复制并且可以扩展到更多表的设计。

按照您的设计方式,当您添加另一种您想要为其添加注释的实体类型时,您无需更改您的模型。此外,您不必在现有模型或其他表中包含任何其他列。

为确保数据完整性,您可以创建一组触发器或一些软件解决方案,它们会不时清理笔记表。

于 2009-07-24T11:40:53.800 回答
1

在做你的建议之前,我会三思而后行。在短期内它可能看起来简单而优雅,但如果您真的对数据完整性和性能感兴趣,那么为每个父表设置单独的注释表是可行的方法。多年来,我使用其他答案(触发器、GUID 等)中找到的解决方案来解决这个问题。我得出的结论是,增加的复杂性和性能损失是不值得的。通过为每个父表设置单独的注释表,并使用适当的外键约束,查找和连接将变得简单而快速。将相关项目组合到一个表中时,连接语法变得丑陋,您的笔记表将变得庞大而缓慢。

于 2009-07-24T12:24:54.177 回答
1

我在一定程度上同意 Michael McLosky 的观点。

我心中的问题是:拥有多个笔记表的技术成本是多少?

在我看来,最好将相同的功能整合到一个表中。它也使报告和其他进一步的开发变得更简单。更不用说保持表格列表更小更易于管理。

这是一种平衡行为,您需要尝试预先确定这样做的好处和成本。我的个人偏好是数据库参照完整性。在我看来,完整性的应用程序管理应该受限于业务逻辑。数据库应确保数据始终一致且有效...


要真正回答你的问题...

我将使用的选项是使用用户定义函数检查值的检查约束。这适用于 M$ SQL Server...

CREATE TABLE Test_Table_1 (id INT IDENTITY(1,1), val INT)
GO
CREATE TABLE Test_Table_2 (id INT IDENTITY(1,1), val INT)
GO
CREATE TABLE Test_Table_3 (fk_id INT, table_name VARCHAR(64))
GO

CREATE FUNCTION id_exists (@id INT, @table_name VARCHAR(64))
RETURNS INT
AS
BEGIN
    IF (@table_name = 'Test_Table_1')
        IF EXISTS(SELECT * FROM Test_Table_1 WHERE id = @id)
            RETURN 1
    ELSE
    IF (@table_name = 'Test_Table_2')
        IF EXISTS(SELECT * FROM Test_Table_2 WHERE id = @id)
            RETURN 1

    RETURN 0
END
GO

ALTER TABLE Test_Table_3 WITH CHECK ADD CONSTRAINT
    CK_Test_Table_3 CHECK ((dbo.id_exists(fk_id,table_name)=(1)))
GO
ALTER TABLE [dbo].[Test_Table_3] CHECK CONSTRAINT [CK_Test_Table_3]
GO

INSERT INTO Test_Table_1 SELECT 1
GO
INSERT INTO Test_Table_1 SELECT 2
GO
INSERT INTO Test_Table_1 SELECT 3
GO
INSERT INTO Test_Table_2 SELECT 1
GO
INSERT INTO Test_Table_2 SELECT 2
GO
INSERT INTO Test_Table_3 SELECT 3, 'Test_Table_1'
GO
INSERT INTO Test_Table_3 SELECT 3, 'Test_Table_2'
GO

在该示例中,最终的插入语句将失败。

于 2009-07-24T13:04:28.073 回答
1

您可以获得 FK 引用完整性,代价是在 notes 表中为每个其他表设置一列。

create table Notes (
    id int PRIMARY KEY,
    note varchar (whatever),
    customer_id int NULL REFERENCES Customer (id),
    product_id int NULL REFERENCES Product (id)
)

然后你需要一个约束来确保你只设置了一个列。

或者可能不是,也许您可​​能希望注释能够与客户和产品相关联。由你决定。

如果您想添加另一个引用表,此设计将需要向 Notes 添加一个新列。

于 2009-07-24T13:07:39.353 回答
0

您可以将 GUID 字段添加到客户、供应商等表中。然后在 Notes 表中,更改外键以引用该 GUID。

这无助于数据完整性。但它使 M 对 N 关系可以轻松地用于任意数量的表,并且使您不必在 Notes 表中定义 NoteFKType 列。

于 2009-07-24T11:37:44.650 回答
0

您可以使用触发器轻松实现“多”外键。触发器将为您提供非常灵活的机制,您可以进行任何您希望的完整性检查。

于 2009-07-24T12:01:23.667 回答
-2

你为什么不反过来做,在其他表(客户、供应商等)中有一个外键到 NotesID。这样你就有了一对一的映射。

于 2009-07-24T11:37:18.347 回答