数据库表,一个表引用多个不相关的表

时间:2010-10-22 17:15:45

标签: sql mysql database-design foreign-key-relationship

在我的搜索中,这个问题在各个论坛中出现了几次,但没有一个提供简洁的解决方案。

如果我有以下表格:

User
+- id
+- username
+- password

Article
+- id
+- title
+- content

我希望加入他们以确定谁创建了哪些文章,我只需将 user_id 列添加到文章即可用作参考。或者,我正在添加一个中间表来显示谁/何时/什么,例如:

User
+- ...

Article
+- ...

ChangeHistory
+- id
+- article_id
+- user_id
+- type [enum(insert, update, delete)]
+- datetime

现在这很好,但我正在研究的系统需要更加动态,因为可以轻松地引入和集成新模块。所以现在如果我添加媒体表,我需要在文章媒体之间拆分 ChangeHistory

User
+- ...

Article
+- ...

Media
+- id
+- title
+- path

ArticleChangeHistory
+- id
+- article_id
+- user_id
+- type [enum(insert, update, delete)]
+- datetime

MediaChangeHistory
+- id
+- media_id
+- user_id
+- type [enum(insert, update, delete)]
+- datetime

通过引入许多模块,这可能会很快失控。每个模块都需要负责创建和管理自己的 ChangeHistory 表。

TL; DR:我可以探索哪些实践来创建一个可以接收多个其他不相关表的引用的中间表?我可以添加一个* record_type *字段,保存记录所属的表的名称,但这很难看。我需要像“表ID ”这样的东西来引用它所来自的表。这样,当添加或删除模块时,模型不会崩溃。

有什么想法吗?非常感谢提前。

5 个答案:

答案 0 :(得分:4)

根据我的经验,当开发人员试图让他们的系统真正“动态”时,他们实际上正在尝试编码他们尚未想到的问题。这通常是一条糟糕的道路。对于模块而言,包含两个表而不是一个表是否真的需要额外的工作?

在每个我看过试图制作通用“做一切”表格的模式(或反模式?)的情况下,它的表面都是平坦的。 RDBMS最适用于明确定义的问题区域。如果模块需要保留历史记录,那么模块应该添加一个历史表来与表本身一起使用。这也有一个巨大的优势,即您可能希望在历史记录中保留不同类型的信息,具体取决于保存历史记录的表或模块。如果您的通用历史表变得更加困难。

现在,如果您想简单地捕获最后一个用户来更新或插入特定项目(表格行)并且可能在多个表中,那么您可以使用一种继承模式,其中您有一个父表和多个子表。例如:

CREATE TABLE Audited_Items
(
    id    INT    NOT NULL    IDENTITY,
    CONSTRAINT PK_Audited_Items PRIMARY KEY CLUSTERED (id)
)
CREATE TABLE Articles
(
    id    INT            NOT NULL,
    [Article specific columns]
    CONSTRAINT PK_Articles PRIMARY KEY CLUSTERED (id),
    CONSTRAINT FK_Articles_Audited_Items FOREIGN KEY (id) REFERENCES Audited_Items (id)
)
CREATE TABLE Media
(
    id    INT            NOT NULL,
    [Media specific columns]
    CONSTRAINT PK_Media PRIMARY KEY CLUSTERED (id),
    CONSTRAINT FK_Media_Audited_Items FOREIGN KEY (id) REFERENCES Audited_Items (id)
)
CREATE TABLE Audit_Trail
(
    audited_item_id    INT         NOT NULL,
    audit_datetime     DATETIME    NOT NULL,
    user_id            INT         NOT NULL,
    [audit columns]
    CONSTRAINT PK_Audit_Trail PRIMARY KEY CLUSTERED (audited_item_id, audit_datetime),
    CONSTRAINT FK_Audit_Trail_Audited_Items FOREIGN KEY (audited_item_id) REFERENCES Audited_Items (id)
)

答案 1 :(得分:2)

在我看来,你正在寻找一个引导你走上扭曲,棘手的道路。

我不知道你为什么每次得到一个新的“模块”时都需要添加一个新表(我不清楚模块是什么),前提是所有模块都可以用相同的列布局来描述。您可以添加Modules表,然后在Articles表中包含ModuleId列。

此外,文章可以有多个作者,所以如果你的作品,你需要一个ArticleAuthors表。在某些圈子中,作者对文章的顺序很重要。这是他们贡献的重要性或他们在该领域的重要性的排名。如果是这种情况,您需要有一个“序数”列来反映作者的立场。

答案 2 :(得分:2)

我认为你可能最好使用一个类似于:

的ChangeHistory表
ItemChangeHistory
+- id
+- changedItem_id
+- user_id
+- changedItemType_id
+- type [enum(insert, update, delete)]
+- datetime

然后使用changedItemType_id根据itemType表找出更改的内容(例如文章,媒体等等)。这样,您只有一个表用于所有内容更改历史记录,并且在添加/删除模块时不必添加/删除表,只需更新itemType表即可。当然,这意味着必须小心地删除模块 - 您可以留下引用该模块类型的ItemChangeHistory记录(但这很麻烦)或删除与该模块关联的所有历史记录。


现在你提到一个模块可能有其他表,所以可能需要跟踪一个子模块ID,也在ItemChangeHistory中引用?

答案 3 :(得分:0)

为什么不

ChangeHistory
+- id
+- content_id
+- content_type
+- user_id
+- action [enum(added, updated, deleted)]
+- datetime

答案 4 :(得分:0)

@Majid: 我知道这可能是最合理的解决方案,但我希望找到一些更直观的东西,这可能会利用现有的数据库参数;例如难以理解的(并且显然不存在)表id(我的意思是某种内部引用,在我的情况下,引擎,InnoDB用于描述表)

@frustrated: 我想我会选择那样的东西。我将继续调查可能性。