2010-02-22 90 views
1

我有一個表,用作事件日誌並存儲用戶登錄狀態'In','Out'或'Rejected'(有時候我的用戶是'根據外部標準被拒絕)。SQL Server檢查Contraint並檢查登錄/註銷狀態

下面是一些示例數據,因此你可以得到的表是什麼樣子的想法:

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 

下面是一些pseduo檢查約束代碼概述我想做什麼:

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

基本上是:

  • 一個人可以,如果他們最後的狀態簽名IN「在」
  • 一個人可以註冊OUT,如果他們最後的狀態是「出」
  • 一個人可以是拒絕的,如果他們最後的狀態是「在」

我不知道檢查約束是否是最好的方法來做到這一點,或者如果我的數據庫設計將允許這個級別的約束;請讓我知道,如果我走了我的心(和好心建議用於存儲數據和/或確保數據的完整性更合適的方法)

注:我使用的是SQL-Server 2008的

回答

2

下面是一個示例觸發器。它假設你只打算每次插入1行(這可能是這種情況),並且我還沒有對索引等產生困擾。

我添加了一個子句,當狀態是'Out '所以它忽略'被拒絕'的狀態 - 這是爲了防止多個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

你必須使用觸發器。

您可以在檢查約束中使用udf來隱藏表訪問。但是不要。

+0

+1,不要使用udf check約束! – 2010-02-22 18:24:42