34

请想象一下这个小型数据库...

图表

删除了死 ImageShack 链接 - 志愿者数据库图

Volunteer     Event         Shift         EventVolunteer
=========     =====         =====         ==============
Id            Id            Id            EventId
Name          Name          EventId       VolunteerId
Email         Location      VolunteerId
Phone         Day           Description
Comment       Description   Start
                            End

协会

志愿者可以报名参加多个活动。
活动可能由多名志愿者组成。

一个事件可能有多个班次。
一个转变只属于一个单一的事件。

一个班次可能只有一名志愿者。
一名志愿者可以工作多个班次。

检查约束

  1. 我可以创建一个检查约束来强制没有一个没有报名参加该轮班活动的志愿者担任轮班工作人员吗?

  2. 我可以创建一个检查约束来强制两个重叠的班次永远不会由同一个志愿者工作吗?

4

4 回答 4

42

加强数据完整性的最佳场所是数据库。请放心,如果您允许,某些开发人员会有意或无意地找到一种方法将不一致的内容偷偷带入数据库!

这是一个带有检查约束的示例:

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)
)
于 2010-04-06T21:21:51.433 回答
2

问题 1 很简单。只需让您的 Shift 表直接引用 EventVolunteer 表即可

于 2010-04-07T01:50:43.120 回答
1

我要做的是在 EventVolunteer 表上有一个自动递增的 Identity 列,对 EventId、VolunteerId 对具有唯一约束。使用 EventVolunteerId(身份)作为 Shift 表的外键。这可以相当简单地强制执行您想要的约束,同时在某种程度上规范化您的数据。

我知道这不是您一般问题的答案,但是我认为这是解决您的具体问题的最佳解决方案。

编辑:

我应该完整阅读这个问题。该解决方案将防止一名志愿者在同一事件中进行两班倒,即使它们不重叠。也许将轮班开始和结束时间移至 EventVolunteer 并对该表的时间进行检查约束就足够了,尽管您在 Shift 表之外有轮班数据,这对我来说听起来并不直观。

于 2010-04-06T21:30:08.607 回答
-5

有一种方法可以通过使用触发器来做到这一点,我不推荐。我建议不要将您的业务逻辑放在数据库级别。db 不需要知道谁在什么时候为某个班次配备人员。该逻辑应该放在您的业务层中。我建议使用存储库构建模式。Scott Gutherie 在他的 mvc 1.0 书中有一个很好的章节描述了这一点(下面的链接)。

http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx

于 2010-04-06T20:59:26.923 回答