如何创建多表检查约束?

时间:2010-04-06 20:31:24

标签: sql-server check-constraints

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

删除了死亡的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 个答案:

答案 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