我有一个包含这些字段的表:

User_id, User_type, User_address

是否可以添加一个约束,其中每个 user_id 只能存在一条记录,其中 user_type = 'xyz'?可以有任意多个 user_type = 'abc',但只有一个 'xyz'。

我知道这不是最好的设计,但它是目前的设计,我需要将其锁定一点。

谢谢


你需要使用触发器...

CREATE TRIGGER yourTriggerName ON YourTableName
AFTER INSERT,UPDATE
AS

IF EXISTS (SELECT
               y.User_id --,COUNT(y.User_Type)
               FROM YourTableName       y 
                   INNER JOIN inserted  i ON y.User_id=i.User_id
               WHERE y.User_Type='xyz'
               GROUP BY y.User_id 
               HAVING COUNT(y.User_Type)>1
          )
BEGIN

    ROLLBACK

END
go

另外,请确保 User_id+User_type 上有索引


一个很常见的问题。我的预设答案:

使用计算列实施复杂的业务规则

您还可以使用索引视图来完成相同的任务。请注意,如果您一次修改多行或者使用快照隔离,则将 UDF 包装在检查约束中可能不起作用:

包含在 CHECK 约束中的标量 UDF 非常慢,并且多行更新可能会失败

为什么我推荐索引而不是触发器?因为如果我有一个索引,我就 100% 确定我的所有数据都是干净的。有了触发器,情况就不是这样了。有时触发器不会触发,有时会出现错误。另一个触发器可以覆盖这个触发器。


我和丹尼尔有同样的想法,但我认为你提出的约束需要检查每个用户最多 1 个 XYZ 类型:

CREATE FUNCTION CheckUserTypeXyzExistAtMostOnce(@User_id int)
RETURNS bit
AS
BEGIN
    DECLARE @count int
    SELECT @count = COUNT(*) FROM dbo.MyTable WHERE User_id = @User_id AND User_type = 'xyz'
    RETURN @count <= 1
END;
ALTER TABLE dbo.MyTableADD CONSTRAINT UserTypeConstraint CHECK (dbo.CheckUserTypeXyzExistAtMostOnce(User_id));

我不确定这是最好的方法,但你总是可以创建一个插入/更新触发器


您可以使用检查约束来强制执行此规则。

CREATE FUNCTION CheckUserTypeXyzExistAtMostOnce()
RETURNS bit
AS 
BEGIN
   DECLARE @count int
   SELECT @count = COUNT(*) FROM dbo.MyTable WHERE UserType = 'xyz'
   RETURN @count <= 1
END;

ALTER TABLE dbo.MyTable
ADD CONSTRAINT UserTypeConstraint CHECK (dbo.CheckUserTypeXyzExistAtMostOnce());

如果我一次只添加一条记录。使用触发器、约束或计算列索引有哪些优点和缺点?也许这应该是另一个问题。

如果您使用或切换到使用快照隔离,这可能会破坏您的触发器,除非您在其中显式设置隔离级别。无论如何,索引都会自动工作。然而,在计算列上使用索引需要进行多项设置,这可能会很不方便。

我决定扣动扳机。我选择触发器的原因之一是当这种情况尝试发生时可以选择使用附加代码。此外,实际上并不需要进行性能调整,而且我认为不需要切换到快照隔离,因此此时不需要设置索引。如果对此表的需求发生变化,则更有可能重新设计架构,并且将不再需要此触发器。感谢大家的时间和回答。

您需要在触发器中显式设置隔离级别,否则切换到快照隔离将破坏它。