Как создать ограничение проверки нескольких таблиц? - PullRequest
24 голосов
/ 07 апреля 2010

Пожалуйста, представьте эту маленькую базу данных ...

Диаграмма

удалена мертвая ссылка ImageShack - диаграмма базы данных добровольцев

Таблица

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

1012 * Ассоциации * Волонтеры могут подписаться на несколько мероприятий.
Мероприятия могут быть укомплектованы несколькими волонтерами. Событие может иметь несколько смен.
Смена относится только к одному событию. В смену может работать только один волонтер.
Волонтер может работать в несколько смен. Проверка ограничений

  1. Могу ли я создать проверочное ограничение для обеспечить, чтобы ни одна смена не была укомплектована волонтер, который не подписан на событие этой смены?

  2. Могу ли я создать проверочное ограничение для обеспечить соблюдение двух пересекающихся смен никогда не укомплектованы одним и тем же доброволец?

Ответы [ 4 ]

31 голосов
/ 07 апреля 2010

Лучшее место для обеспечения целостности данных - это база данных. Будьте уверены, что какой-то разработчик, намеренно или нет, найдет способ проникнуть непоследовательным материалом в базу данных, если вы позволите им!

Вот пример с проверочными ограничениями:

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)
)
2 голосов
/ 07 апреля 2010

Вопрос 1 прост. Просто пусть ваша таблица Shift ссылается непосредственно на таблицу EventVolunteer, и все готово

1 голос
/ 07 апреля 2010

То, что я хотел бы сделать, это иметь столбец Identity в таблице EventVolunteer, который автоматически увеличивается, с уникальным ограничением для пары EventId, VolunteerId. Используйте EventVolunteerId (identity) в качестве внешнего ключа для таблицы Shift. Это обеспечивает достаточно простое ограничение, в то время как ваши данные несколько нормализуются.

Я понимаю, что это не ответ на ваш общий вопрос, однако я считаю это лучшим решением вашей конкретной проблемы.

Редактировать:

Я должен был прочитать вопрос полностью. Это решение не позволит одному добровольцу выполнять две смены на одном и том же мероприятии, даже если они не перекрываются. Возможно, будет достаточно перенести время начала и окончания смены в EventVolunteer и иметь ограничение проверки времени для этой таблицы, хотя тогда у вас есть данные сдвига вне таблицы Shift, которые не кажутся мне понятными.

0 голосов
/ 07 апреля 2010

Есть способ сделать это с помощью триггеров, которые я бы не рекомендовал.Я бы порекомендовал не размещать вашу бизнес-логику на уровне базы данных.БД не нужно знать кто, укомплектовывает определенную смену и в какое время.Эта логика должна быть помещена в ваш слой бизнеса.Я бы порекомендовал использовать шаблон построения хранилища.У Скотта Гютери есть очень хорошая глава в его книге о mvc 1.0, которая описывает это (ссылка ниже).

...