SQL Server проверяет ограничения и проверяет состояние входа / выхода - PullRequest
1 голос
/ 22 февраля 2010

У меня есть таблица, которая работает как журнал событий и хранит пользователей, подписанных в состоянии «В», «Вне» или «Отклонено» (иногда пользователи могут быть «Отклонены» на основании внешних критериев).

Вот несколько примеров данных, чтобы вы могли понять, как выглядит таблица:

Table MyTable
PersonID - State       - DateTime
// data sample
156      - 'Out'       - 02-14-2010 13:04:15
156      - 'In'        - 02-21-2010 09:01:13
16       - 'In'        - 02-21-2010 09:05:01
58       - 'Rejected'  - 02-21-2010 11:04:58
156      - 'Out'       - 02-21-2010 11:10:02

Вот некоторый код ограничения проверки псевдо-проверки, описывающий то, что я хотел бы сделать:

CHECK(
        CASE
            WHEN (
                [State] = 'In' AND
                (Select TOP 1 State FROM MyTable WHERE PersonID=@PersonID_ToUpdate)!='In' ORDER BY DateTime DESC)
            )
            THEN 'T'
            WHEN (
                [State] = 'Out' AND
                (Select TOP 1 State FROM MyTable WHERE PersonID=@PersonID_ToUpdate)!='Out' ORDER BY DateTime DESC)
            )
            THEN 'T'
            WHEN (
                [State] = 'Rejected' AND
                (Select TOP 1 State FROM MyTable WHERE PersonID=@PersonID_ToUpdate)!='In' ORDER BY DateTime DESC)
            )
            THEN 'T'
            ELSE 'F'
        END = 'T'
)

В основном:

  • Человек может подписать IN , если его последнее состояние было , а не 'In'
  • Человек может подписать OUT , если его последнее состояние было , а не 'Out'
  • Человек может быть ОТКАЗАН , если его последнее состояние было , а не 'В'

Я не знаю, является ли Проверка Ограничения лучшим способом сделать это, или моя структура базы данных учитывает этот уровень ограничения; пожалуйста, дайте мне знать, если я не в своем уме (и, пожалуйста, предложите более подходящий метод для хранения данных и / или обеспечения целостности данных)

примечание: я использую SQL-Server 2008

Ответы [ 2 ]

2 голосов
/ 22 февраля 2010

Вот пример триггера. Предполагается, что вы будете вставлять только 1 строку за раз (что, вероятно, имеет место здесь), и я не беспокоился об индексах и т. Д.

Я добавил условие, когда состояние «Out», поэтому оно игнорирует «отклоненные» состояния - это должно было предотвратить несколько выходов. Это очень просто, но вы поняли.

if object_id('dbo.MyTable') is not null     
    drop table dbo.MyTable;

create table dbo.MyTable (
    PersonID int not null,
    [State] varchar(20) not null,
    [DateTime] datetime not null default(getdate())
    ); 

if object_id('dbo.ins_MyTable_status_validation') is not null drop trigger dbo.ins_MyTable_status_validation;
go
create trigger dbo.ins_MyTable_status_validation
    on dbo.MyTable
    instead of insert
as 
begin
    set nocount on;

    -- assuming you're only inserting 1 row at a time (which makes sense for an event log)
    if (select count(*) from inserted) > 1 begin
        print 'Multiple rows inserted - raise some kind of error and die'
        return
    end

    declare @personid_toupdate int,
            @state varchar(20);

    select  @personid_toupdate = personid,
            @state = [state]
    from    inserted;

    if case
        when (
            @state = 'In' and
            isnull((select top 1 [State] from dbo.MyTable where personid = @personid_toupdate order by [datetime] desc), 'Blah') != 'In'
            )
            then 'T'
        when (
            @state = 'Out' and
            isnull((select top 1 [State] from dbo.MyTable where personid = @personid_toupdate and [State] != 'Rejected' order by [datetime] desc), 'Blah') != 'Out' 
            )
            then 'T'
        when (
            @state = 'Rejected' and
            isnull((select top 1 [State] from dbo.MyTable where personid = @personid_toupdate order by [datetime] desc), 'Blah') != 'In'
            )
            then 'T'
            else 'F'
        end = 'T'
    begin
        -- data is valid, perform the insert
        insert  dbo.MyTable (PersonID, [State]) 
        select  PersonID, [State]
        from    inserted; 
    end
    else
    begin
        -- data is invalid, return an error (something a little more informative than this perhaps)
        raiserror('bad data...', 16, 1)
    end
end
go

-- test various combinations to verify constraints
insert  dbo.MyTable (PersonID, [State]) values (1, 'In')
insert  dbo.MyTable (PersonID, [State]) values (1, 'Out')
insert  dbo.MyTable (PersonID, [State]) values (1, 'Rejected')

select * from dbo.MyTable
1 голос
/ 22 февраля 2010

Вы должны использовать триггер.

Вы можете использовать udf в проверочном ограничении, чтобы скрыть доступ к таблице. Но не надо.

...