【问题标题】:How to add a unique constraint on several columns, with a condition?如何使用条件在多个列上添加唯一约束?
【发布时间】:2012-02-23 11:54:53
【问题描述】:

问题:

我想在映射表 (n:n) 上添加唯一约束。
我希望可以插入新值,但前提是 TEST_FK_UID、TEST_DateFrom 和 TEST_DateTo 不等于现有条目。

问题在于状态字段。
状态 1 表示活动..
状态!= 1 表示不活动/已删除..
.
.
因此,当然可以插入具有相同 FK、DateFrom 和 DateTo 的新条目,如果 - 并且仅当 - 现有条目的状态(所有现有条目,您可以插入、删除、插入、删除、插入、删除,等)是!= 1

这是我目前所拥有的:

CREATE TABLE dbo._________Test  
(
     TEST_UID uniqueidentifier NOT NULL 
    ,TEST_FK_UID uniqueidentifier NOT NULL 
    ,TEST_DateFrom DateTime NOT NULL 
    ,TEST_DateTo DateTime NOT NULL  
    ,TEST_Status int NOT NULL 
    ,UNIQUE(TEST_FK_UID, TEST_DateFrom, TEST_DateTo, TEST_Status) 
); 

【问题讨论】:

标签: sql sql-server sql-server-2008 sql-server-2005 tsql


【解决方案1】:

你不能。但是,您可以创建一个唯一的 index。它的功能类似,我希望对你来说足够好。

CREATE UNIQUE INDEX MyIndex
ON _________Test
( TEST_FK_UID
, TEST_DateFrom
, TEST_DateTo )
WHERE TEST_Status = 1

唯一索引和唯一约束之间最重要的区别在于,您不能在另一个引用唯一索引的表中创建外键。 编辑:as Martin指出,这是不正确的,外键可以引用未经过滤的唯一索引。

【讨论】:

  • 这不是唯一索引和唯一约束之间的区别。这是过滤和非过滤唯一索引之间的区别。
  • 太棒了!什么都比触发器好。
  • @MartinSmith 嗯,你是对的,引用唯一索引的外键确实有效。谢谢。那么文档的某些部分需要更新。
  • @hvd 你能指出文档的其他含义吗?
  • @AaronBertrand msdn.microsoft.com/en-us/library/ms175464.aspx "FOREIGN KEY 约束不必只链接到另一个表中的 PRIMARY KEY 约束;它也可以定义为引用另一个表中的 UNIQUE 约束的列。”从技术上讲,这是真的,但它具有误导性。
【解决方案2】:

在 INSERT、UPDATE 操作上使用而不是触发器。 并使用 INSERTED 表中的值检查现有值(在触发器的情况下创建) 如果 INSERTED 表中的状态 id 为 1 并且它是唯一的,则执行插入操作或只是中止一些消息..

【讨论】:

    【解决方案3】:

    很有可能,像这样
    (基本功劳:https://stackoverflow.com/users/103075):


    编辑:
    好的,学究式地看到它不是唯一约束,它是检查约束,但是 WTF - 它具有相同的效果并且也适用于 SQL-Server 2005,并且(条件)条件可以为每个客户配置(替换 SET @bNoCheckForThisCustomer = ' false' 与选择配置表) - 这是不可能的唯一索引 AFAIK ... ;)

    注意这一行:

    AND ZO_RMMIO_UID != @in_ZO_RMMIO_UID
    

    (ZO_RMMIO_UID是n:n映射表的唯一主键)
    这很重要,因为检查约束似乎类似于 onAfterInsert 触发器。
    如果缺少这一行,它也会检查自身,这会导致函数始终返回 true...


    IF  EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[CheckNoDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_AP_Raum_AP_Ref_Mietobjekt]'))
    ALTER TABLE [dbo].[T_ZO_AP_Raum_AP_Ref_Mietobjekt] DROP CONSTRAINT [CheckNoDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt]
    GO
    
    
    
    
    
    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fu_InsertCheck_IsDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
    DROP FUNCTION [dbo].[fu_InsertCheck_IsDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt]
    GO
    
    
    
    
    
    -- ========================================================================
    -- Author:            Me
    -- Create date:       09.08.2010
    -- Last modified:     09.08.2010
    -- Description:   Conditionally check if row is a duplicate
    -- ========================================================================
    
    -- PRE:  UID, Valid RM_UID, Valid MIO_UID, 
    --       Valid datetime-from for db usr language, valid datetime-to for db usr language
    -- POST: True/False
    
    
    CREATE  FUNCTION [dbo].[fu_InsertCheck_IsDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt](@in_ZO_RMMIO_UID uniqueidentifier, @in_ZO_RMMIO_RM_UID AS uniqueidentifier, @in_ZO_RMMIO_MIO_UID as uniqueidentifier, @in_ZO_RMMIO_DatumVon AS datetime, @in_ZO_RMMIO_DatumBis AS datetime)
        RETURNS bit
    AS
        BEGIN   
    
            DECLARE @bIsDuplicate AS bit
            SET @bIsDuplicate = 'false'     
    
    
            DECLARE @bNoCheckForThisCustomer AS bit
            SET @bNoCheckForThisCustomer = 'false'
    
            IF @bNoCheckForThisCustomer = 'true'
                RETURN @bIsDuplicate 
    
    
    
    
            IF EXISTS
            (
                SELECT 
                     ZO_RMMIO_UID
                    ,ZO_RMMIO_RM_UID
                    ,ZO_RMMIO_MIO_UID
                FROM T_ZO_AP_Raum_AP_Ref_Mietobjekt 
                WHERE ZO_RMMIO_Status = 1 
                AND ZO_RMMIO_UID != @in_ZO_RMMIO_UID
                AND ZO_RMMIO_RM_UID = @in_ZO_RMMIO_RM_UID 
                AND ZO_RMMIO_MIO_UID = @in_ZO_RMMIO_MIO_UID 
                AND ZO_RMMIO_DatumVon = @in_ZO_RMMIO_DatumVon 
                AND ZO_RMMIO_DatumBis = @in_ZO_RMMIO_DatumBis 
            )
                SET @bIsDuplicate = 'true'
    
            RETURN @bIsDuplicate
        END
    
    
    GO
    
    
    
    
    ALTER TABLE [dbo].[T_ZO_AP_Raum_AP_Ref_Mietobjekt]  WITH NOCHECK ADD  CONSTRAINT [CheckNoDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt] 
    CHECK  
    (
        NOT 
        (
            dbo.fu_InsertCheck_IsDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt(ZO_RMMIO_UID, ZO_RMMIO_RM_UID, ZO_RMMIO_MIO_UID, ZO_RMMIO_DatumVon, ZO_RMMIO_DatumBis) = 1 
        )
    )
    GO
    
    
    ALTER TABLE [dbo].[T_ZO_AP_Raum_AP_Ref_Mietobjekt] CHECK CONSTRAINT [CheckNoDuplicate_T_ZO_AP_Raum_AP_Ref_Mietobjekt]
    GO
    

    这里是一个测试用例:

    CREATE TABLE [dbo].[T_ZO_AP_Raum_AP_Ref_Mietobjekt](
        [ZO_RMMIO_UID] [uniqueidentifier] NOT NULL,  -- <== PRIMARY KEY
        [ZO_RMMIO_RM_UID] [uniqueidentifier] NOT NULL,
        [ZO_RMMIO_MIO_UID] [uniqueidentifier] NOT NULL,
        [ZO_RMMIO_DatumVon] [datetime] NOT NULL,
        [ZO_RMMIO_DatumBis] [datetime] NOT NULL,
        [ZO_RMMIO_Status] [int] NOT NULL,
        [ZO_RMMIO_Bemerkung] [text] NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    
    
    /*
    DELETE FROM T_ZO_AP_Raum_AP_Ref_Mietobjekt 
    WHERE ZO_RMMIO_Status = 1 
    AND ZO_RMMIO_RM_UID = '2007B6F5-9010-4979-AB39-00057DA353C0' 
    AND ZO_RMMIO_MIO_UID = 'FFA177E9-971E-4500-805D-00116F708E7B'
    */
    
    
    INSERT INTO T_ZO_AP_Raum_AP_Ref_Mietobjekt
    (
         ZO_RMMIO_UID
        ,ZO_RMMIO_RM_UID
        ,ZO_RMMIO_MIO_UID
        ,ZO_RMMIO_DatumVon
        ,ZO_RMMIO_DatumBis
        ,ZO_RMMIO_Status
        ,ZO_RMMIO_Bemerkung
    )
    VALUES
    (
         NEWID() --<ZO_RMMIO_UID, uniqueidentifier,>
        ,'2007B6F5-9010-4979-AB39-00057DA353C0' --<ZO_RMMIO_RM_UID, uniqueidentifier,>
        ,'FFA177E9-971E-4500-805D-00116F708E7B' --<ZO_RMMIO_MIO_UID, uniqueidentifier,>
        ,'01.01.2012' --<ZO_RMMIO_DatumVon, datetime,>
        ,'31.12.2999' --<ZO_RMMIO_DatumBis, datetime,>
        ,1 --<ZO_RMMIO_Status, int,>
        ,NULL--<ZO_RMMIO_Bemerkung, text,>
    )
    GO
    
    
    
    INSERT INTO T_ZO_AP_Raum_AP_Ref_Mietobjekt
    (
         ZO_RMMIO_UID
        ,ZO_RMMIO_RM_UID
        ,ZO_RMMIO_MIO_UID
        ,ZO_RMMIO_DatumVon
        ,ZO_RMMIO_DatumBis
        ,ZO_RMMIO_Status
        ,ZO_RMMIO_Bemerkung
    )
    VALUES
    (
         NEWID() --<ZO_RMMIO_UID, uniqueidentifier,>
        ,'2007B6F5-9010-4979-AB39-00057DA353C0' --<ZO_RMMIO_RM_UID, uniqueidentifier,>
        ,'FFA177E9-971E-4500-805D-00116F708E7B' --<ZO_RMMIO_MIO_UID, uniqueidentifier,>
        ,'01.01.2012' --<ZO_RMMIO_DatumVon, datetime,>
        ,'31.12.2999' --<ZO_RMMIO_DatumBis, datetime,>
        ,1 --<ZO_RMMIO_Status, int,>
        ,NULL--<ZO_RMMIO_Bemerkung, text,>
    )
    GO
    
    SELECT [ZO_RMMIO_UID]
          ,[ZO_RMMIO_RM_UID]
          ,[ZO_RMMIO_MIO_UID]
          ,[ZO_RMMIO_DatumVon]
          ,[ZO_RMMIO_DatumBis]
          ,[ZO_RMMIO_Status]
          ,[ZO_RMMIO_Bemerkung]
      FROM [T_ZO_AP_Raum_AP_Ref_Mietobjekt]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-02
      • 2011-02-11
      相关资源
      最近更新 更多