【问题标题】:Can I conditionally enforce a uniqueness constraint?我可以有条件地强制执行唯一性约束吗?
【发布时间】:2013-08-20 01:17:57
【问题描述】:

我的数据库包含一个用户表。每个活跃用户都有一个唯一的用户名。我希望能够停用用户并释放他们正在使用的用户名,但将它们保留在同一个表中。

有没有办法只有条件地强制执行唯一性约束?

【问题讨论】:

  • 让什么成为可能? UI 是否将已删除的用户显示为“USER DELETED”,或者该表中的该列多次包含相同的值?
  • @delnan:鉴于标题,我假设是后者。
  • 只需创建字段 - 类似于状态(开 - 关)。或 dtime(删除 lol 的时间)。或者...
  • @Cthulhu 你是说字段?
  • @WhozCraig:一种可能性是可能有活动记录、交易记录、消息等。出于历史目的需要保留。也许“已删除”真的意味着“禁用和存档”之类的东西。 (这可以追溯到克苏鲁关于标志或日期时间的单独列的想法。

标签: mysql unique-constraint


【解决方案1】:

我会创建另一个名为 FORMER_NAME 的(非唯一)字段,并在用户处于非活动状态时将原始名称移动到该字段。不需要特殊的唯一性约束,这是不可能的。

【讨论】:

    【解决方案2】:

    不同的方式达到相同的结果。对于所提出的问题,可能并不真正需要。但仅供参考。

    1. 在插入/更新时创建触发器
    2. 检查是否存在具有当前(新)记录值的重复记录。
      一个。这可以通过计算重复项或检查是否存在具有相同值但主键不同的其他记录来检查
    3. 如果发现引发Signal 以引发错误

    如果您的条件很难确定唯一性,这最适合。还要考虑性能成本。

    示例

    DELIMITER $$
    
    CREATE TRIGGER `my_trigger` BEFORE INSERT/UPDATE
        ON `usertable`
        FOR EACH ROW BEGIN
    
        IF EXISTS (SELECT 1 FROM usertable WHERE userid <> NEW.userid AND username = NEW.username AND isactive = 1) THEN 
            SELECT CONCAT(NEW.username, ' exists !') INTO @error_text; 
    
    
    
         SIGNAL SQLSTATE '45000' SET message_text = @error_text; 
        END IF;
        END$$
    
    DELIMITER ;
    

    【讨论】:

    • 这还能用吗?我不断收到 mysql 语法错误?
    • 这是一个非常粗鲁的回应。
    • 我不确定什么是粗鲁的回应。请尝试理解该策略并在较新版本的 mysql 中找到等效项。这个答案发布于 2017 年 10 月。我确信 mysql 在那之后发生了变化。
    【解决方案3】:

    添加另一个名为isactive 的列。在(username, isactive) 上创建唯一约束。

    然后您可以同时拥有一个活动和非活动用户名。您将无法拥有两个活动用户名。

    如果您想要多个非活动名称,请使用NULL 作为isactive 的值。 NULL 值可以在唯一索引中重复。

    【讨论】:

    • 如果曾经选择过一个答案,它应该是这个,用于实际回答所提出的问题。
    • 是否使用 NULL 而不是“false”来绕过被认为是不好的做法的约束?
    • @theyuv 。 . . null 在唯一约束中的行为取决于数据库。出于这个原因,我不喜欢那个解决方案。
    • 对更好的解决方案有什么建议吗?我有一列IsEnabled 在给定一组额外的约束(例如:userid)的情况下,我希望只启用一个行,但同一用户可以有多个禁用的行。这个答案 (stackoverflow.com/questions/4617914/…) 提出了一个我不理解的替代方案。
    • @theyuv 。 . .您可能想就您的具体问题提出问题。
    【解决方案4】:

    不,UNIQUE 约束不能是“有条件的”。

    一种选择是将username 列设置为NULL。 UNIQUE 约束将允许具有 NULL 值的多行。

    您可以将其转换为您想要显示的任何字符串。无论是在应用程序中,还是在 SQL 中

    SELECT IFNULL(t.username,'USER DELETED') AS username
      FROM mytable t
    

    如果您出于历史/存档目的保留这些行,您可能不想更新username 列。 (如果您更改了username 列的值,那么后续语句将被允许插入与之前的用户名具有相同值的行。)

    您可以改为在表中添加一个附加列,以表示“用户已删除”条件。例如:

    user_deleted TINYINT(1) UNSIGNED DEFAULT 0 COMMENT 'boolean' 
    

    只要设置了 user_deleted 布尔值,您就可以检查此列并返回 'USER DELETED' 常量来代替用户名列:

    SELECT IF(u.user_deleted,'USER DELETED',u.username) AS username
    

    (使用值 1 表示逻辑“用户已删除”条件。)

    这种方法的最大优点是不必修改username 列、username 值和 UNIQUE 约束将防止插入具有重复用户名的新行。

    【讨论】:

    • +1 表示唯一列中允许的唯一可重复值(我只是在上面的一般评论中推测。我从未在 MySQL 中尝试过;只有 SQL Server,但很高兴知道它是可能的)。
    【解决方案5】:

    不,如果有唯一索引(因此得名),则不能有重复项。添加额外的列以使每条记录唯一。或更改值使其唯一。

    不推荐,但例如您可以添加时间戳“USER DELETED 2013/08/17:233805”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 2018-03-07
      • 1970-01-01
      • 2014-06-20
      • 1970-01-01
      • 1970-01-01
      • 2018-09-11
      相关资源
      最近更新 更多