【问题标题】:UNIQUE Constraint, only when a field contains a specific valueUNIQUE 约束,仅当字段包含特定值时
【发布时间】:2011-06-24 18:01:38
【问题描述】:

我正在尝试为两列创建 UNIQUE INDEX 约束,但仅当另一列包含值 1 时。例如,column_1column_2 应仅在 active = 1 时为 UNIQUE。任何包含active = 0 的行都可以与另一行共享column_1column_2 的值,而不管另一行的active 值是什么。但是active = 1 的行不能与具有active = 1 的另一行共享column_1column_2 的值。

我所说的“共享”是指在同一列中具有相同值的两行。示例:row1.a = row2.a AND row1.b = row2.b。仅当 row1 中的两列都与 row2 中的其他两列匹配时,才会共享值。

我希望我说清楚了。 :\

【问题讨论】:

    标签: mysql unique-constraint


    【解决方案1】:

    您可以尝试使用 column_1、column_2 和 active 来创建多列 UNIQUE 索引,然后为不需要唯一性的行设置 active=NULL。或者,您可以使用触发器(see MySQL trigger syntax) 并检查每个插入/更新的行是否已经在表中 - 但我认为这会很慢。

    【讨论】:

    • 我推荐 NULL 解决方案 - 这很简单,NULL 有点等于 FALSE(只是不要对原教旨主义的 SQLer 这么说),最重要的是它可以让你的数据库完成所有工作你。
    • 在NULL解决方案中,数据库在2行相同的情况下如何区分它们的唯一性?那个时候mysql会避免约束检查吗?
    【解决方案2】:

    我正在尝试为两列创建一个 UNIQUE INDEX 约束,但前提是另一列包含值 1

    您可以将“另一列”的值设置为不等于 1 的唯一值。例如记录的 id。

    然后唯一索引约束可以应用于所有三列,包括“另一列”。让我们称“另一列”columnX。 如果要将唯一约束应用于记录,请将 columnX 的值设置为 1。如果您不想应用唯一约束,请将 columnX 的值设置为唯一值。

    然后不需要额外的工作/触发器。所有三列的唯一索引可以解决您的问题。

    【讨论】:

    • 当设置 NULL 不是一个选项时的绝妙主意!
    【解决方案3】:

    我不确定 MySQL 的语法,但它应该与 SQL Server 有几乎相同的东西:

    CREATE UNIQUE INDEX [UNQ_Column1Column2OnAtive_MyTable]
      ON dbo.[MyTable]([column1,column2)
      WHERE   ([active] = 1);
    

    此索引将确保如果 active=1 则 column1 和 column2 组合在整个表中是唯一的。

    【讨论】:

      【解决方案4】:

      在 SQL Server 中,这可以通过检查约束来完成,但是我不知道 MySQL 是否支持类似的东西。

      适用于任何数据库的方法是,您可以将表一分为二。如果 active =0 的记录只是历史记录,并且永远不会再次激活,您可以将它们移动到另一个表,并在原始表上设置一个简单的唯一约束。

      【讨论】:

      • 我在 IRC 上得到了类似的建议;创建一个活动表和一个非活动表。但是他们用你说的方式更有意义;有一个历史表和一个活动表。但是,该表有很多字段,将这些字段复制到两个表中似乎是错误的做法。你会建议我如何避免这种冗余?
      • 一点点非规范化可以大有帮助,所以不用担心重复的定义。当然,您还必须对原始表和历史表进行任何更改,所以这是一些开销,但是,恕我直言,干净的数据非常值得。
      • 嗯,如果我只分离需要唯一索引的数据呢?表:mydata、mydata_active、mydata_inactive。这样 mydata 包含主键和数据列。而且,mydata_active 和 mydata_inactive 将只包含需要唯一索引的列和引用 mydata 的外键。这是另一种方式,但它需要一个额外的(第三个)表。
      • 这也可以,但是开销可能太大,因为任何涉及这些表的查询都必须有一个额外的连接。如果历史数据很少被使用,你基本上应该把它们藏在角落里,然后忘记它们。
      • 参加聚会很晚,但补充了@SWeko 的答案:如果您使用的是 Microsoft SQL Server,另请参阅此处建议的过滤索引:stackoverflow.com/a/5149263/3766034
      【解决方案5】:

      我不确定我是否 100% 了解您,但假设您有一个包含状态列的表,并且您想确保只有一个原始状态为“A”(活动)。您可以处理许多状态为“I”或“Z”或其他任何行的行。只允许一行状态为“A”。

      这样就可以了。

        CREATE UNIQUE INDEX [Idx_EvalHeaderOnlyOneActive]
        ON [dbo].[EvalHeader]([Hdr Status])
        WHERE [Hdr Status] = 'A';
      

      【讨论】:

        【解决方案6】:

        索引不受外部影响。这种约束必须在您的数据库之外实现。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-13
          • 2021-03-10
          • 1970-01-01
          • 1970-01-01
          • 2023-02-13
          • 1970-01-01
          • 2018-05-09
          • 2021-03-06
          相关资源
          最近更新 更多