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

发布于 2024-09-29 03:38:38 字数 1168 浏览 5 评论 0原文

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

如果我有以下表格:

User
+- id
+- username
+- password

Article
+- id
+- title
+- content

并且我想加入它们以确定谁创建了哪些文章,我可以简单地将 user_id 列添加到 Article 中以用作参考。或者,我添加一个中间表来显示谁/何时/什么,例如:

User
+- ...

Article
+- ...

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

现在这很好,但我正在开发的系统需要更加动态,因为可以轻松引入和集成新模块。因此,现在如果我添加 Media 表,我需要将 ChangeHistory 拆分为 ArticleMedia ,其中

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”之类的东西来引用它所来自的表。这样,当/如果添加或删除模块时,模型就不会崩溃。

有什么想法吗?预先非常感谢。

This question has come up a few times in various forums in my searching, but none have provided a concise resolution.

If I have the following tables:

User
+- id
+- username
+- password

Article
+- id
+- title
+- content

and I want to join them in order to determine who created what articles, I could simply add the column user_id to Article to use as a reference. Alternatively, I'm adding an intermediate table to show who/when/what, for example:

User
+- ...

Article
+- ...

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

Now this is fine, but the system I'm working on needs to be much more dynamic, in that new modules can be easily introduced and integrated. So now if I add a Media table I need to split the ChangeHistory between Article and Media having:

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

This can get out of hand quickly with the introduction of many modules. Each module would need to be responsible for the creation and management of it's own ChangeHistory table.

TL;DR: What practices can I explore to create an intermediate table that can receive references to multiple other unrelated tables? I could add a *record_type* field, holding the name of the table to which the record belongs, but that's ugly. I would need something like a "table ID" to reference the table from which it's coming. That way, when/if modules are added or removed, the model doesn't fall apart.

Any ideas? Thanks so much in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

青柠芒果 2024-10-06 03:38:38

根据我的经验,当开发人员试图使他们的系统真正“动态”时,他们实际上是在尝试为他们尚未想到的问题编写代码。这通常是一条不好的路。对于一个模块来说,包含两个表而不是一个表真的需要做很多额外的工作吗?

在我见过的每种情况下,尝试制作一个通用的“做所有事情”的表格的模式(或反模式?),它都失败了。 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)
)

In my experience, when developers try to make their system really "dynamic" they're actually trying to code for problems that they haven't thought of yet. That's usually a bad path to take. Is it really so much extra work for a module to include two tables instead of one?

In every case where I've seen the pattern (or anti-pattern?) of trying to make a generic "does everything" table it's fallen flat on its face. RDBMSs work best with well-defined problem areas. If the module has a need to keep history then the module should add a history table to go with the table itself. This also has a huge advantage in that down the road you're likely to want to keep different types of information in the history depending on the table or module for which the history is being kept. If you have a generic history table that becomes much more difficult.

Now, if you want to simply capture the last user to update or insert a particular item (table row) and that could be in multiple tables then you could use a pattern of inheritance where you have a parent table and multiple children tables. For example:

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)
)
美人迟暮 2024-10-06 03:38:38

在我看来,你似乎在寻求一位向导来引导你走上一条曲折、布满荆棘的道路。

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

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

It seems to me you're asking for a guide to lead you down a twisting, thorny path.

I don't know why you'd need to add a new table every time you get a new "module" (not clear to me what a module is) provided the modules can all be described with the same column layout. You could add a Modules table and then include a ModuleId column in the Articles table.

Also, articles can have more than one author, so if yours do, you'd need an ArticleAuthors table. And in some circles, the order of authors on an article is important. It's a ranking of the importance of their contribution or of their importance in the field. If that's the case, you'd need to have an "ordinal" column to reflect the author's position.

失而复得 2024-10-06 03:38:38

我认为您可能最好使用一个类似于以下内容的 ChangeHistory 表:

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

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


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

I think you might be best off with a single ChangeHistory table that looks something like:

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

and then use changedItemType_id to figure out what was changed (eg. article, media, etc...) based on an itemType table. This way you only have one table for all content change histories, and you don't have to add/remove tables when you add/remove modules, you simply update the itemType table. Of course this means that removing a module has to be done carefully - you could leave behind the ItemChangeHistory records that reference that module's type (but that's messy) or remove all history associated with the module.


Now you mention a module may have other tables, so maybe those need to be tracked with a submodule ID, also referenced in ItemChangeHistory?

我不咬妳我踢妳 2024-10-06 03:38:38

为什么不呢

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

Why not

ChangeHistory
+- id
+- content_id
+- content_type
+- user_id
+- action [enum(added, updated, deleted)]
+- datetime
没有伤那来痛 2024-10-06 03:38:38

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

@frustrated:
我想我会接受类似的事情。我会继续调查可能性。

@Majid:
I understand that's likely the most reasonable solution, but I was hoping to find something a bit more intuitive, something that could potentially take advantage of existing DB parameters; such as the elusive (and clearly non-existent) table id (by which I mean some sort of internal reference that the engine, InnoDB in my case, uses to describe tables)

@frustrated:
I suppose I'm going to go with something like that. I'm going to keep investigating possibilities.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文