请想象一下这个小型数据库......
删除了死亡的ImageShack链接 - 志愿者数据库图
Volunteer Event Shift EventVolunteer
========= ===== ===== ==============
Id Id Id EventId
Name Name EventId VolunteerId
Email Location VolunteerId
Phone Day Description
Comment Description Start
End
志愿者可以报名参加多项活动 活动可能由多名志愿者组成。
活动可能有多班次 转变只属于一个事件。
班次可能只由一名志愿者组成 志愿者可以多班次。
我可以创建一个检查约束 强制执行没有班次 一个没有报名参加的志愿者 那个班次的事件?
我可以创建一个检查约束 强制执行两个重叠的转变 从来没有相同的人员 志愿者?
答案 0 :(得分:31)
强制数据完整性的最佳位置是数据库。请放心,有些开发人员,无论是否有意,都会找到一种方法,如果你让他们将不一致的东西偷偷带进数据库!
以下是检查约束的示例:
CREATE FUNCTION dbo.SignupMismatches()
RETURNS int
AS BEGIN RETURN (
SELECT count(*)
FROM Shift s
LEFT JOIN EventVolunteer ev
ON ev.EventId = s.EventId
AND ev.VolunteerId = s.VolunteerId
WHERE ev.Id is null
) END
go
ALTER TABLE Shift ADD CONSTRAINT chkSignup CHECK (dbo.SignupMismatches() = 0);
go
CREATE FUNCTION dbo.OverlapMismatches()
RETURNS int
AS BEGIN RETURN (
SELECT count(*)
FROM Shift a
JOIN Shift b
ON a.id <> b.id
AND a.Start < b.[End]
AND a.[End] > b.Start
AND a.VolunteerId = b.VolunteerId
) END
go
ALTER TABLE Shift ADD CONSTRAINT chkOverlap CHECK (dbo.OverlapMismatches() = 0);
以下是新数据完整性检查的一些测试:
insert into Volunteer (name) values ('Dubya')
insert into Event (name) values ('Build Wall Around Texas')
-- Dubya tries to build a wall, but Fails because he's not signed up
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Wall', '2010-01-01', '2010-01-02')
-- Properly signed up? Good
insert into EventVolunteer (VolunteerID, EventID)
values (1, 1)
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Wall', '2010-01-01', '2010-01-03')
-- Fails, you can't start the 2nd wall before you finished the 1st
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Second Wall', '2010-01-02', '2010-01-03')
以下是表格定义:
set nocount on
if OBJECT_ID('Shift') is not null
drop table Shift
if OBJECT_ID('EventVolunteer') is not null
drop table EventVolunteer
if OBJECT_ID('Volunteer') is not null
drop table Volunteer
if OBJECT_ID('Event') is not null
drop table Event
if OBJECT_ID('SignupMismatches') is not null
drop function SignupMismatches
if OBJECT_ID('OverlapMismatches') is not null
drop function OverlapMismatches
create table Volunteer (
id int identity primary key
, name varchar(50)
)
create table Event (
Id int identity primary key
, name varchar(50)
)
create table Shift (
Id int identity primary key
, VolunteerId int foreign key references Volunteer(id)
, EventId int foreign key references Event(id)
, Description varchar(250)
, Start datetime
, [End] datetime
)
create table EventVolunteer (
Id int identity primary key
, VolunteerId int foreign key references Volunteer(id)
, EventId int foreign key references Event(id)
, Location varchar(250)
, [Day] datetime
, Description varchar(250)
)
答案 1 :(得分:2)
问题1很简单。只需让您的Shift表直接引用EventVolunteer表,就可以了。
答案 2 :(得分:1)
我要做的是在EventVolunteer表上有一个自动递增的Identity列,对EventId,VolunteerId对有唯一约束。使用EventVolunteerId(标识)作为Shift表的外键。这样可以非常简单地强制执行约束,同时对数据进行一定程度的规范化。
我知道这不是您一般性问题的答案,但我认为这是解决您具体问题的最佳方法。
编辑:
我应该完整地阅读这个问题。该解决方案将阻止一名志愿者在同一事件中进行两次轮班,即使他们没有重叠。也许将班次的开始和结束时间移动到EventVolunteer并对该表上的时间进行检查约束就足够了,尽管你在Shift表之外有移位数据,这对我来说听起来并不直观。
答案 3 :(得分:-4)
有一种方法可以通过使用触发器来实现,我不建议这样做。我建议不要将你的商务逻辑放在数据库级别。数据库不需要知道谁,在哪个时间点配备某个班次。这个逻辑应该放在你的商业层。我建议使用存储库构造模式。 Scott gutherie在他的mvc 1.0书中有一个很好的章节,描述了这一点(下面的链接)。
http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx