乐队和成员之间的关系,' MemberOf',包括会员ID(中),乐队ID(出价),' StartYear' EndYear'和'仪器'播放。 Mid和Bid分别是会员和乐队关系的外键,但我很难在这种情况下找出会员和会员的主键。关系。
注意:如果成员在开始和结束年份的乐队中,如果成员仍在乐队中,则开始年份也可以为null。
在这种情况下,起始年份和结束年份不能用作PK,因为它们可能包含空值,但成员ID和band id不足以使其成为唯一的 - 例如同一成员日后返回同一乐队。
这是否意味着没有主键存在?感谢。
答案 0 :(得分:3)
这是一个常见问题。要了解发生了什么,我们需要从SQL构造PRIMARY KEY中解开关系概念“主键”。
关系概念“主键”是表中行的唯一标识符。在你的桌子的上下文中,乐队和成员显然是主键的一部分,成员为乐队演奏的时间也是如此。一个成员可以参加2003-2005赛季的特定乐队,2006-2007的单人生涯,并重新加入2008-2011赛季的原创乐队。表中的成员需要两个条目,每个条目对应两个句点。
理想情况下,您可以将'startYear'和'endYear'形成一个'memberFor'期间列,主键是(bid,mid,memberFor),对于bid + mid的单一组合, memberFor值表示不相交的范围:即,对于两行R1,R2,值R1.memberFor不重叠或触摸R2.memberFor。示例重叠:R1.memberFor =(2003-2005),R2.Memberfor =(2004-2006)。触摸示例:R1.memberFor =(2003-2005),R2.MemberFor =(2005-2007)。
这个理论太多了。
实际上,SQL不支持时间段,其中此上下文中的时间段具有开始和持续时间。 INTERVAL类型不符合条件,因为它们表示没有开始的持续时间,DATE,TIME,TIMESTAMP类型不符合条件,因为它们缺少持续时间(忽略与手头问题没有密切关系的量化问题)。显然,您还可以表示句点(持续时间,结束)以及(开始,结束),在后一种情况下,您可以为开始和结束设置打开,关闭,打开 - 关闭或关闭 - 打开的范围。
因为SQL不支持必需的类型,更不用说必要的检查选项,所以你必须自己进行检查,这样做很复杂。 SQL主键仅查找值的相等性。不受约束,这意味着如果您在(出价,中间,开始)或(出价,中间,结束)或(出价,中间,开始,结束)创建主键,您可以在表中满足不满意的数据SQL主键但不是概念主键。
Bid Mid Start End
1 1 2003 2005 - α
1 1 2004 2006 - β
1 1 2004 2007 - γ
1 1 2008 2011 - δ
将标记为α的行视为正确。如果您的SQL主键已打开(bid,mid,start),则不应允许标记为β的行,因为它与α重叠;同样适用于γ。但是,SQL只能阻止β和γ出现;首先输入的那个将是正常的,输入的第二个将被拒绝。如果允许γ,则不应允许∂,因为它接触γ。同样,如果SQL主键处于打开状态(bid,mid,start,end),则所有四行都将被允许进入表中,但显然不应该这样。如果SQL主键已打开(bid,mid,end),则允许上面的所有行。
因此,您必须使用筛选出不需要的行的复杂查询来扩充SQL主键的条件。这将是用于表的插入触发器和更新触发器中的存储过程。
...详细信息留给读者练习......
在您的表格中,您有一个基于年份的粒度。因此,您可能可以使用仅存储(mid,bid,memberInYear)的修订表,并且当每个乐队成员都是给定乐队的成员时,每个乐队成员都有一行。这减少到大量子时间段,其中三列上的SQL主键强制执行约束。但是,如果您更改表格的粒度以记录开始和结束日期(截至当天),那么修改后的设计显然效果不佳。
因此,您应该使用插入和更新触发器调用的存储过程来支持(bid,mid,start)上的SQL主键,这些存储过程会强制执行非重叠和非触摸条件。
答案 1 :(得分:1)
注意:如果是,开始年份可以为null 会员从一开始就在一个乐队中
嗯,这个荒谬的要求是你问题中最重要的部分。存储年份。
{BandId,MemberId,StartYear}作为主键。
这不会容纳2月份开始,3月份退出,7月份重新开始的会员。
答案 2 :(得分:0)
你是对的,日期不能被视为PK的一部分,因为它们是随时间变化的数据。我认为你有两个选择:
1)将MemberOf记录拆分为包含子表的父子对,可以称为MemberOfYears,包含Mid,Bid和a Year,父对象MemberOf只是成为Mid&出价。因此,每个年份都会包含一个MemberOfYears记录,该成员是该特定乐队的成员。
2)将一个人工密钥添加到MemberOf关系表中,并将其用作主要密钥。
答案 3 :(得分:0)
一种可能的解决方案是将StartYear值存储为频段启动的年份。这样你可以使用(MId,BId,StartYear)作为主键,假设成员每年只能加入和退出一次(如设计所示)。
要测试“原始成员”,请将MemberOf.StartYear与Band.StartYear进行比较以确保相等。
否则不行,你没有有效的主键,需要添加某种消除歧义的列(JoinNumber或其他东西),这似乎是不必要的额外努力。
答案 4 :(得分:0)
StartYear
可以不为空 - 真的。如果您引入IsCurrent
而不是EndYear
,那么根本就没有NULL。如果您确实选择保留EndYear
,则通常会在未来加载某个日期,例如3000-01-01
。最后,如果你保留并留下EndYear
NULL - 这不是悲剧,唯一的问题是可能很难区分缺失的数据和当前的成员资格。
注意:StartDate
可能比StartYear
更合适。