【问题标题】:TSQL check constraint related to existing rows与现有行相关的 TSQL 检查约束
【发布时间】:2015-09-05 00:43:18
【问题描述】:

我有下表:

CREATE TABLE dbo.myTable] 
(
    ID1 [int] NOT NULL,
    ID2 [int] NOT NULL,
    StartDate smalldatetime NOT NULL,
    EndDate smalldatetime NULL,
    CONSTRAINT [PK_myTable1] PRIMARY KEY CLUSTERED (ID1 ASC, ID2 ASC, StartDate ASC)
) ON [PRIMARY]

我想确保每个 ID1 和 ID2 的 StartDateEndDate 期间是唯一的,并且没有重叠。

如何创建这样的检查约束:

(
    ID1 <> existingRow.ID1
    or ID2 <> existingRow.ID2
)
or (
    ID1 = existingRow.ID1
    and ID2 = existingRow.ID2
    and (
        StartDate >= isnull(existingRow.EndDate, Startdate + 1)
        or isnull(EndDate, existingRow.StartDate + 1) <= existingRow.StartDate
    )
)

...或具有如下条件的约束:

IF ID1 = existingRow.ID1 and ID2 = existingRow.ID2
CHECK (
    StartDate >= isnull(existingRow.EndDate, Startdate + 1)
    or isnull(EndDate, existingRow.StartDate + 1) <= existingRow.StartDate
)

提前谢谢...

【问题讨论】:

  • 您必须使用触发器进行此类验证
  • 我不太了解您的伪代码,但您实际上是在寻找temporal table 吗?也就是说,在任何给定的时刻,给定的ID1ID2 组合只有一个“有效”行?
  • 如果是这样,您可能会发现this answer很有用
  • 我将尝试 ughai 的“在插入/更新触发器之前”的想法。谢谢... ;)
  • 我链接到的答案似乎比“仅”使用触发器更有效,但具有实际正确性已在表上的约束中建模的优点。该答案中的触发器只是隐藏了一些移动部件。

标签: sql-server tsql constraints


【解决方案1】:

您可以有两个表:一个只存储 ID1 和 ID2 列,另一个复制您现在拥有的表。然后,您可以向第一个表添加一系列触发器,在插入、更新或删除之后,这些触发器将插入或更新第二个表。大致如下:

CREATE TABLE dbo.TableOne
(
    ID1 [int] NOT NULL,
    ID2 [int] NOT NULL,
    CONSTRAINT [PK_myTable1] PRIMARY KEY CLUSTERED (ID1 ASC, ID2 ASC)
) ON [PRIMARY]

GO
create table TableTwo(
    ID1 int not null,
    ID2 int not null,
    StartDate smalldatetime not null default(getdate()),
    EndDate smalldatetime not null
)
GO
create trigger tTableOneAfterInsert on TableOne after insert
as begin
    update TableTwo 
        set EndDate = getdate()
    from TableTwo 
        join inserted on TableTwo.ID1 = inserted.ID1 and TableTwo.ID2 = inserted.ID2
    where TableTwo.EndDate is NULL

    insert into TableTwo(ID1, ID2)
    select ID1, ID2 from inserted
end
GO
create trigger tTableOneAfterUpdate on TableOne after update
as begin
    update TableTwo 
        set EndDate = getdate()
    from TableTwo 
        join inserted on TableTwo.ID1 = inserted.ID1 and TableTwo.ID2 = inserted.ID2
    where TableTwo.EndDate is NULL

    insert into TableTwo(ID1, ID2)
    select ID1, ID2 from inserted
end
GO
create trigger tTableOneAfterDelete on TableOne after delete
as begin
    update TableTwo 
        set EndDate = getdate()
    from TableTwo 
        join deleted on TableTwo.ID1 = deleted.ID1 and TableTwo.ID2 = deleted.ID2
    where TableTwo.EndDate is NULL
end

【讨论】:

    【解决方案2】:

    你尝试过这样的事情吗?

    CREATE FUNCTION dbo.fn_check_unique_value 
    (
        @ID1 INT,
        @ID2 INT,
        @StartDate SMALLDATETIME,
        @EndDate SMALLDATETIME
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @Result INT
    
        SET @Result = (SELECT COUNT(*) 
                       FROM   dbo.myTable existingRow
                       WHERE  (   @ID1 <> existingRow.ID1
                                 or @ID2 <> existingRow.ID2
                                )
                                or (
                                    @ID1 = existingRow.ID1
                                    and @ID2 = existingRow.ID2
                                    and (
                                         @StartDate >= isnull(existingRow.EndDate, @Startdate + 1)
                                         or isnull(@EndDate, existingRow.StartDate + 1) <= existingRow.StartDate
                                        )
                                   ))
    
        RETURN @Result
    END
    GO
    
    ALTER TABLE dbo.myTable ADD CONSTRAINT CK_No_Overlap
        CHECK ( dbo.fn_check_unique_value(ID1, ID2, StartDate, EndDate) <= 1
              )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 2015-04-13
      • 1970-01-01
      • 2017-12-29
      • 2013-08-26
      • 2011-06-10
      相关资源
      最近更新 更多