多个但互斥的外键 - 这是要走的路吗?
我有三个表:用户、公司和网站。 用户和公司都有网站,因此每个用户记录都有一个外键进入网站表。此外,每条公司记录都有一个进入 Websites 表的外键。
现在我想将 Websites 表中的外键包含回其各自的“父”记录中。我该怎么做?我是否应该在每个网站记录中有两个外键,其中之一始终为 NULL?或者还有别的路可走吗?
I have three tables: Users, Companies and Websites.
Users and companies have websites, and thus each user record has a foreign key into the Websites table. Also, each company record has a foreign key into the Websites table.
Now I want to include foreign keys in the Websites table back into their respective "parent" records. How do I do that? Should I have two foreign keys in each website record, with one of them always NULL? Or is there another way to go?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
如果我们查看这里的模型,我们将看到以下内容:
第三种关系意味着存在一个“用户或公司”实体,其
PRIMARY KEY
应存储在某处。要存储它,您需要创建一个表来存储
网站所有者
实体的主键
。该表还可以存储用户和网站共有的属性。由于它是一对一的关系,因此网站属性也可以存储在该表中。
用户和公司不共享的属性应存储在单独的表中。
要强制建立正确的关系,您需要将
website
的PRIMARY KEY
与owner type
组合在一起作为其一部分,并强制具有CHECK
约束的子表中的正确类型:If we look into the model here, we will see the following:
The third relation implies existence of a "user or company" entity whose
PRIMARY KEY
should be stored somewhere.To store it you need to create a table that would store a
PRIMARY KEY
of awebsite owner
entity. This table can also store attributes common for a user and a website.Since it's a one-to-one relation, website attributes can be stored in this table too.
The attributes not shared by users and companies should be stored in the separate table.
To force the correct relationships, you need to make the
PRIMARY KEY
of thewebsite
composite withowner type
as a part of it, and force the correct type in the child tables with aCHECK
constraint:您不需要父列,您可以通过在用户和公司表上进行简单的选择(或连接表)来查找父列。如果您想知道这是用户还是公司网站,我建议在您的网站表中使用布尔列。
you don’t need a parent column, you can lookup the parents with a simple select (or join the tables) on the users and companies table. if you want to know if this is a user or a company website i suggest using a boolean column in your websites table.
为什么您需要从网站到用户/公司的外键?不重复数据的原则表明,最好扫描用户/公司表以查找匹配的网站 ID。如果您确实需要,您始终可以在网站表中存储一个标志,表示给定的网站记录是针对用户还是针对公司,然后扫描适当的表。
Why do you need a foreign key from website to user/company at all? The principle of not duplicating data would suggest it might be better to scan the user/company tables for a matching website id. If you really need to you could always store a flag in the website table that denotes whether a given website record is for a user or a company, and then scan the appropriate table.
我对接受的答案(Quassnoi)的问题是,对象关系是错误的:公司不是网站所有者的子类型;公司不是网站所有者的子类型。在拥有网站之前我们就已经有了公司,而且我们可以拥有作为网站所有者的公司。另外,在我看来,网站所有权是网站与个人或公司之间的关系,即我们应该在架构中拥有一个(或两个)关系表。将个人网站所有权与公司网站所有权分开并仅在需要时将它们放在一起(例如通过
VIEW
)可能是一种可接受的方法:上述问题是没有办法使用数据库约束来强制执行网站由个人或公司所有但不能同时由两者所有的规则。
如果我们可以假设 DBMS 强制执行检查约束(正如已接受的答案所做的那样),那么我们可以利用一个(人)人和一家公司都是法人的事实,并使用一个超类型表(
LegalPersons),但仍然保留关系表方法 (
WebsiteOwnership
),这次使用VIEW
将个人网站所有权与公司网站所有权分开,但这次使用强类型属性:我们需要的是“分布式外键”的新 DBMS 功能(“对于此表中的每一行,其中一个表中必须恰好有一行”)和“多重赋值”,以允许将数据添加到表中限制在单个 SQL 语句中。可悲的是,我们距离获得这样的功能还有很长的路要走!
The problem I have with the accepted answer (by Quassnoi) is that the object relationships are the wrong way around: company is not a sub-type of a website owner; we had companies before we had websites and we can have companies who are website owners. Also, it seems to me that website ownership is a relationship between a website and either a person or a company i.e. we should have a relationship table (or two) in the schema. It may be an acceptable approach to keep personal website ownership separate from corporate website ownership and only bring them together when required e.g. via
VIEW
s:The problem with the above is there is no way of using database constraints to enforce the rule that a website is either owned by a person or a company but not both.
If we can assuming the DBMS enforces check constraints (as the accepted answer does) then we can exploit the fact that a (human) person and a company are both legal persons and employ a super-type table (
LegalPersons
) but still retain relationship table approach (WebsiteOwnership
), this time using theVIEW
s to separate personal website ownership from separate from corporate website ownership but this time with strongly typed attributes:What we need are new DBMS features for 'distributed foreign keys' ("For each row in this table there must be exactly one row in one of these tables") and 'multiple assignment' to allow the data to be added into tables thus constrained in a single SQL statement. Sadly we are a far way from getting such features!
首先,您真的需要这个双向链接吗?除非绝对需要,否则最好避免使用它。
据我了解,您想知道该网站是属于用户还是属于公司。您可以通过在 Website 表中添加一个简单的布尔字段 - [BelongsToUser] 来实现这一点。如果为 true,那么您会查找用户,如果为 false,您会查找一家公司。
First of all, do you really need this bi-directional link? It is a good practice to avoid it unless absolutely needed.
I understand it that you wish to know whether the site belongs to a user or to a company. You can achieve that by having a simple boolean field in the Website table - [BelongsToUser]. If true, then you look up a user, if false - you look up a company.
有点晚了,但所有现有的答案似乎都有些达不到目标:
1:Many
关系1:1
关系CHECK
约束(不强制执行引用完整性)其中一些在 < a href="https://stackoverflow.com/a/40218845/7412956" title="Other Answer">来自 onedaywhen 的答案,但该答案仍然错过了让 MySQL 承担繁重工作并强制执行的机会参照完整性。
无论如何,一个网站在法律上只能有一个所有者。一个人或公司可以拥有任意数量的网站,也可以没有。数据库中从所有者到网站的链接在任何标准化级别都只能是
1:1
。实际上,关系是1:Many
,并且需要为恰好拥有多个网站的每个所有者拥有多个表条目。从数据库术语和现实情况来看,从网站到所有者的链接都是1:1
。拥有从网站到所有者的链接可以更好地代表模型。通过网站表中的索引,对给定所有者执行1:Many
查找变得相当高效。SQL 中的
CHECK
属性将是一个很好的解决方案,如果 MySQL 没有碰巧默默地忽略它的话。MySQL 文档 13.1.20创建表语法
MySQL 的功能确实提供了两种解决方案作为解决方案来实现
CHECK
行为并保持数据的引用完整性。带有存储过程的触发器就是其中之一,并且可以很好地适应各种约束。虽然通用性较差,但更容易实现的是使用VIEW
和WITH CHECK OPTION
子句,MySQL 将实现。MySQL 文档 24.5 .4 视图WITH CHECK OPTION子句
MySQLTUTORIAL 站点在其 SQL 简介中提供了这两个选项的一个很好的示例检查约束教程。 (您必须考虑拼写错误,但否则很好。)
在尝试解决类似的互斥外键拆分并开发解决方案时发现了这个问题,并根据答案生成了提示,似乎只适合在以下位置分享我的解决方案返回。
推荐的解决方案
为了将对现有架构和访问数据的应用程序的影响降至最低,请按原样保留
Users
和Companies
表。重命名Websites
表并将其替换为应用程序可以继续访问的名为Websites
的 VIEW。除了处理所有权信息外,所有对Websites
的旧查询应该仍然有效。因此:设置
使用
标准化级别
作为标准化的技术说明,可以从 Websites 表中提取所有权信息,并创建一个新表来保存所有权数据,包括 is_normal 列。
然而,我相信,所创建的 VIEW 及其限制可以防止规范化旨在消除的任何异常情况,并增加情况下不需要的复杂性。无论如何,标准化过程始终是一种权衡。
A bit late, but all the existing answers seemed to fall somewhat short of the mark:
1:Many
relation1:1
relationCHECK
constraints on tables (no enforcement of referential integrity)Some of this is recognized in the answer from onedaywhen, yet that answer still missed the opportunity to make MySQL do the heavy lifting and enforce the referential integrity.
A website can only have one owner, legally, anyway. A person, or company, can have any number of websites, including none. A link in the database from owner to website can only be
1:1
at any level of normalization. In reality the relation is1:Many
, and would require having multiple table entries for each owner that happens to own more than one website. A link from website to owner is1:1
in both database terms and in reality. Having the link from website to owner represents the model better. With an index in the website table, doing the1:Many
lookup for a given owner becomes reasonably efficient.The
CHECK
attribute in SQL would be an excellent solution, if MySQL didn't happen to silently ignore it.MySQL Docs 13.1.20 CREATE TABLE Syntax
MySQL's functionality does offer two solutions as work-arounds to implement the behavior of
CHECK
and keep the referential integrity of the data. Triggers with stored procedures is one, and works well with all manner of constraints. Easier to implement, though less versatile, is using aVIEW
with aWITH CHECK OPTION
clause, which MySQL will implement.MySQL Docs 24.5.4 The View WITH CHECK OPTION Clause
The MySQLTUTORIAL site gives a good example of both options in their Introduction to the SQL CHECK constraint tutorial. (You have to think around the typos, but good otherwise.)
Having found this question while trying to resolve a similar mutually exclusive foreign key split and developing a solution, with hints generated by the answers, it seems only proper to share my solution in return.
Recommended Solution
For the minimum impact to the existing schema, and the application accessing the data, retain the
Users
andCompanies
tables as they are. Rename theWebsites
table and replace it with a VIEW namedWebsites
which the application can continue to access. Except when dealing with the ownership information, all the old queries toWebsites
should still work. So:The setup
Usage
Normalization Level Up
As a technical note for normalization, the ownership information could be factored out of the Websites table and a new table created to hold the ownership data, including the is_normal column.
I believe, however, that the created VIEW, with its constraints, prevents any of the anomalies that normalization aims to remove, and adds complexity that is not needed in the situation. The normalization process is always a trade off anyway.