表中的单个默认值

时间:2012-06-11 11:15:55

标签: sql sql-server

我正在创建一个只有一位非空列IsDefault的表。我需要编写一个约束,确保每个UserId(同一个表中的字段)只有一个默认值。 我不能对此使用唯一约束,因为它可能有许多非默认值。

使用MS SQL Server 2008执行此操作的最佳方法是什么?

感谢。

7 个答案:

答案 0 :(得分:3)

我看到的最简单方法是使用UDF(用户定义函数)检查约束。

例如,请看这里。 http://sqljourney.wordpress.com/2010/06/25/check-constraint-with-user-defined-function-in-sql-server/

未经测试的例子

CREATE FUNCTION dbo.CheckDefaultUnicity(@UserId int)
RETURNS int
AS 
BEGIN
   DECLARE @retval int
   SELECT @retval = COUNT(*) FROM <your table> where UserId = @UserId and <columnwithDefault> = 1-- or whatever is your default value
   RETURN @retval 
END;
GO

并改变你的表格

ALTER TABLE <yourTable> 
ADD CONSTRAINT Ck_UniqueDefaultForUser 
CHECK (dbo.CheckDefaultUnicity(UserId) <2)

答案 1 :(得分:1)

另一个相对简单的选择是使用CLUSTERED INDEXED VIEW。其中的要点是

  • 从您的表格中选择UserID中的所有IsDefault=1
  • UserID
  • 上添加唯一索引

聚集索引视图

CREATE VIEW dbo.VIEW_Users_IsDefault WITH SCHEMABINDING AS 
  SELECT  UserID, IsDefault
  FROM    dbo.Users
  WHERE   IsDefault = 1
GO  

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_USERS_ISDEFAULT 
  ON dbo.VIEW_Users_IsDefault (UserID)
GO

测试脚本

BEGIN TRAN

CREATE TABLE dbo.Users (UserID INT, IsDefault BIT)
GO

CREATE VIEW dbo.VIEW_Users_IsDefault WITH SCHEMABINDING AS 
  SELECT  UserID, IsDefault
  FROM    dbo.Users
  WHERE   IsDefault = 1
GO  

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_USERS_ISDEFAULT ON dbo.VIEW_Users_IsDefault (UserID)
GO

INSERT INTO dbo.Users VALUES (1, 0)
INSERT INTO dbo.Users VALUES (1, 1)
INSERT INTO dbo.Users VALUES (1, 1)  -- Fails because of clustered index

ROLLBACK TRAN

答案 2 :(得分:1)

检查约束肯定会起作用,但在我看来,这不是一个好的设计选择。原因是您的约束的UDF类似于

SELECT @Count = COUNT(UserId) 
FROM   User
WHERE  IsDefault = 1
GROUP BY UserId
HAVING COUNT(UserId) > 1

IF @Count > 0 
  ....'FAIL

因为这涉及2列,所以它需要是一个表级约束,而你所记录的记录越多,插入/更新/删除就会越慢。

更好的选择是只允许通过存储过程访问该表,因此在插入/更新之前,您可以非常快速地运行

IF EXISTS(SELECT UserId FROM User where UserId = @UserId and IsDefault = 1)
在插入/更新/删除之前

但是我可以理解您可能正在使用ORM并且可能不希望在系统中安装Stored Procs,因此您可以将表的设计更改为以下内容。这假定

tblUser:UserId,FirstName,Suraname等

tblUserDefault: UserId (唯一约束)

我不确定IsDefault在你的系统中代表什么,所以我在上面假设用户是默认的或不是默认的。任何人都可以使用它作为参考。它允许您在不使用USP或可怕的表范围检查约束(或触发器)的情况下强制执行约束,并且可以在任何正常的ORM中映射

答案 3 :(得分:0)

CHECK Constraints怎么样?见这里:http://msdn.microsoft.com/en-us/library/ms188258(v=sql.105).aspx

ALTER TABLE yourtable
ADD CONSTRAINT IsDefaultChecked CHECK (IsDefault = T );

答案 4 :(得分:0)

在这种情况下,您可能希望使用触发器。当用户更改其默认值时,触发器可以自动将当前用户的当前默认值翻转为false。

基本上,对于插入/更新中IsDefault值设置为1的任何用户,使用AFTER插入/更新触发器将IsDefault列设置为0。

CREATE TRIGGER dbo.tr_default
ON dbo.MyTable
AFTER INSERT, UPDATE 
AS

  if(exists(select * from inserted where IsDefault = 1)
  begin

      update dbo.MyTable
        set IsDefault = 0
      from inserted i
      join dbo.MyTable t on i.userid = t.userid
      where i.IsDefault = 1
        and i.TheValue != t.TheValue

  end

答案 5 :(得分:0)

到目前为止,我认为CHECK CONSTRAINTSTRIGGERS的任何答案都没有任何问题,但对我来说这似乎是一个倒退的解决方案。

我只能假设您的表中的UserIDUser表的外键,那么为什么不在User表中添加一列来存储默认的CardID,而不是将其标记为默认值?这使得用户无法在没有昂贵的触发器/约束的情况下拥有多个默认CardID。如果你使列不可为空,那么如果你愿意的话,用户也不可能没有默认的CardID。

答案 6 :(得分:0)

虽然我认为触发器和约束解决方案更好,但如果您通过存储过程控制插入/更新,则更简单的方法是首先更新冲突的行(假设新的默认值总是获胜):

ALTER PROCEDURE dbo.UserWhateverTable_<action>
  @UserID INT,
  @CardID INT
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.UserWhatever 
      SET IsDefault = 0 
      WHERE UserID = @UserID
        AND CardID = @CardID
        AND IsDefault = 1;

    -- insert or update here
END
GO

事实上,除了使用触发器或约束来保护它之外,执行此操作(因此业务逻辑在DML过程中100%清晰)可能并不是一个坏主意(以捕获更新在外部进行更新的情况)你的程序)。