多个表的外键
我的数据库中有 3 个相关表。
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)
用户属于多个组。这是通过多对多关系完成的,但在本例中无关紧要。通过 dbo.Ticket.Owner 字段,票证可由组或用户拥有。
描述票证与用户或组(可选)之间关系的最正确方式是什么?
我想我应该在票证表中添加一个标志,说明什么类型拥有它。
I've got 3 relevant tables in my database.
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)
Users belong to multiple groups. This is done via a many to many relationship, but irrelevant in this case. A ticket can be owned by either a group or a user, via the dbo.Ticket.Owner field.
What would be the MOST CORRECT way describe this relationship between a ticket and optionally a user or a group?
I'm thinking that I should add a flag in the ticket table that says what type owns it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您有几个选择,所有选择的“正确性”和易用性各不相同。一如既往,正确的设计取决于您的需求。
您可以简单地在工单中创建两列 OwnedByUserId 和 OwnedByGroupId,并为每个表
您可以创建 M:M 引用表,以启用工单:用户和工单:组关系。也许将来您会希望允许多个用户或组拥有一张票证?此设计并不强制要求票证必须仅由单个实体拥有。
您可以为每个用户创建一个默认组,并让票证仅归真正的组或用户的默认组所有。
或者(我的选择)建模一个充当用户和组基础的实体,并拥有该实体拥有的票证。
这是使用您发布的架构的一个粗略示例:
You have a few options, all varying in "correctness" and ease of use. As always, the right design depends on your needs.
You could simply create two columns in Ticket, OwnedByUserId and OwnedByGroupId, and have nullable Foreign Keys to each table.
You could create M:M reference tables enabling both ticket:user and ticket:group relationships. Perhaps in future you will want to allow a single ticket to be owned by multiple users or groups? This design does not enforce that a ticket must be owned by a single entity only.
You could create a default group for every user and have tickets simply owned by either a true Group or a User's default Group.
Or (my choice) model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity.
Heres a rough example using your posted schema:
@Nathan Skerl 列表中的第一个选项是已实现的选项在我曾经参与过的一个项目中,三个表之间建立了类似的关系。 (其中一个引用了另外两个,一次一个。)
因此,引用表有两个外键列,并且它还有一个约束来保证单行引用一个表(不是两个,也不是两个)。 。
应用于表格时的外观如下:
如您所见,
Ticket
表有两列,OwnerGroup
和OwnerUser
,两者都是它们是可为空的外键。 (其他两个表中的相应列相应地成为主键。)CK_Ticket_GroupUser
检查约束确保两个外键列中只有一个包含引用(另一个为 NULL,这就是为什么两者都有可以为空)。(
Ticket.ID
上的主键对于此特定实现来说不是必需的,但在这样的表中拥有一个绝对不会有什么坏处。)The first option in @Nathan Skerl's list is what was implemented in a project I once worked with, where a similar relationship was established between three tables. (One of them referenced two others, one at a time.)
So, the referencing table had two foreign key columns, and also it had a constraint to guarantee that exactly one table (not both, not neither) was referenced by a single row.
Here's how it could look when applied to your tables:
As you can see, the
Ticket
table has two columns,OwnerGroup
andOwnerUser
, both of which are nullable foreign keys. (The respective columns in the other two tables are made primary keys accordingly.) TheCK_Ticket_GroupUser
check constraint ensures that only one of the two foreign key columns contains a reference (the other being NULL, that's why both have to be nullable).(The primary key on
Ticket.ID
is not necessary for this particular implementation, but it definitely wouldn't harm to have one in a table like this.)另一种方法是创建一个关联表,其中包含每个潜在资源类型的列。在您的示例中,两个现有所有者类型中的每一个都有自己的表(这意味着您有一些可以引用的内容)。如果情况始终如此,您可以采用如下方式:
使用此解决方案,您将在向数据库添加新实体时继续添加新列,并且删除并重新创建 @Nathan Skerl 显示的外键约束模式。该解决方案与@Nathan Skerl 非常相似,但看起来不同(取决于偏好)。
如果您不打算为每个新所有者类型创建一个新表,那么最好为每个潜在所有者包含一个owner_type而不是外键列:
使用上述方法,您可以添加尽可能多的所有者类型想。 Owner_ID 没有外键约束,但将用作对其他表的引用。缺点是您必须查看表才能了解所有者键入的内容,因为根据架构,它并不是立即显而易见的。仅当您事先不知道所有者类型并且它们不会链接到其他表时,我才会建议这样做。如果您事先知道所有者类型,我会采用像@Nathan Skerl 这样的解决方案。
抱歉,如果我弄错了一些 SQL,我只是将它们放在一起。
Another approach is to create an association table that contains columns for each potential resource type. In your example, each of the two existing owner types has their own table (which means you have something to reference). If this will always be the case you can have something like this:
With this solution, you would continue to add new columns as you add new entities to the database and you would delete and recreate the foreign key constraint pattern shown by @Nathan Skerl. This solution is very similar to @Nathan Skerl but looks different (up to preference).
If you are not going to have a new Table for each new Owner type then maybe it would be good to include an owner_type instead of a foreign key column for each potential Owner:
With the above method, you could add as many Owner Types as you want. Owner_ID would not have a foreign key constraint but would be used as a reference to the other tables. The downside is that you would have to look at the table to see what the owner types there are since it isn't immediately obvious based upon the schema. I would only suggest this if you don't know the owner types beforehand and they won't be linking to other tables. If you do know the owner types beforehand, I would go with a solution like @Nathan Skerl.
Sorry if I got some SQL wrong, I just threw this together.
另一种选择是在
Ticket
中,有一列指定所属实体类型(User
或Group
),第二列包含引用的用户
或组
id,不使用外键,而是依靠触发器来强制引用完整性。与内森的优秀模型(上图)相比,我在这里看到了两个优点:
Yet another option is to have, in
Ticket
, one column specifying the owning entity type (User
orGroup
), second column with referencedUser
orGroup
id and NOT to use Foreign Keys but instead rely on a Trigger to enforce referential integrity.Two advantages I see here over Nathan's excellent model (above):
您还可以使用枚举来识别
Owner
是用户还是组,如下所示:也许它并不比任何建议的解决方案更好,它可能不会提供任何优势。事实上,我认为这可能需要更改
Enum_OwnerType
甚至ticket
才能更改OwnerType
,我想......我希望它有用反正。you can also use an enum to identify whether
Owner
is user or group like this:Maybe it's no better than any of proposed solutions, it might not offer any advantage. In fact, I think that this might require altering
Enum_OwnerType
and eventicket
in order to changeOwnerType
, I guess... I hope it's useful anyway.我有很多这样的情况,我只是使用如下所示的多态能力:
示例
我有营业额表,其中包含此列
id
、amount
、user_id
我需要知道每条记录的引用,所以我只需添加两个字段table_id
和table_type
,我的最终营业额表就像id
,金额
,user_id
、table_id
、table_type
。[
1
,25000
,2
,22
,order
][
1
,25000
,2
,23
,credit
],请注意
如果使用 M :M表需要花费很多时间两次检索记录
而我的方式
I have many cases like this and I just use polymorphic ability like below:
example
I have turnovers table that have this columns
id
,amount
,user_id
and I need to know the refrence of every records, So I just add two Fieldstable_id
andtable_type
and my final turnovers table is likeid
,amount
,user_id
,table_id
,table_type
.[
1
,25000
,2
,22
,order
][
1
,25000
,2
,23
,credit
]note
if using M:M tables its take so much time two retrieve the records
and my way
nathan_jr 的第四个选项(对充当用户和组的基础的实体进行建模,并拥有该实体拥有的票证)不会强制 PartyId 的引用完整性。您必须在应用程序层上执行此操作,这会带来各种麻烦。当 django 的 genericforeignkey 实现相同的解决方案时,不能真正将其称为反模式,但毫无疑问,您可以使用框架的 orm 设计更强大和高性能的东西(使用 django 的多表继承之类的东西)
nathan_jr's 4th option (model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity) doesn't enforce referential integrity on PartyId. You'd have to do that on the application layer which invites all sorts of trouble. Can't really call it an antipattern when django's genericforeignkey implements the same solution, but no doubt you can design something more robust and performant using your framework's orm (using something like django's Multi-table inheritance)
一种解决方案是使用独立于数据记录的记录键。我们为每条记录使用一个 GUID。对几个表之一的外键约束是通过保留对其进行外键约束的“键”表来完成的。在这种情况下,组和用户将各自包含一个必须存在于密钥表中的唯一密钥,可能伴有一个代码来指示所属表。票证外键将引用键表。我们有一个参数化存储过程来插入记录,如果提交的记录中不存在该记录,则该记录会获取新的键值。新的键被插入到键表中,然后新的记录被插入到相应的表中。现有的键会导致插入异常(应该是更新。)
One solution is to use record keys independent of data record. We use a GUID for each record. Foreign key constraint to one of several tables is accomplished by keeping a table of "keys" to which foreign key constraint is made. In case at point Group and User would each contain a unique key that must exist in key table, possibly accompanied by a code to indicate owning table. Ticket foreign key would reference key table. We have a parameterized stored procedure to insert records that obtains a new key value if one is not present in submitted record. New key is inserted in key table, then new record is inserted in respective table. An existing key would cause an exception on insert (should be an update.)
我认为这将是表示您想要的内容的最通用方法,而不是使用标志。
I think that would be the most general way to represent what you want instead of using a flag.