繁体   English   中英

如何在布尔 MySQL 列上创建“唯一”约束?

[英]How to create a “unique” constraint on a boolean MySQL column?

我想将一个BOOLEAN列添加到一个名为is_default的 MySQL 表中。 在此列中,只有一条记录可以将is_default设置为true

如何使用 MySQL 将此约束添加到我的列中?

谢谢!


更新

如果这不是我应该添加的约束。 我们如何处理 DB 上的此类问题?

我认为这不是模拟单个默认值情况的最佳方式。

相反,我会保留 IsDefault 列并创建一个单独的表,其中包含一行且仅包含构成主表主键的列。 在此表中放置标识默认记录的 PK 值。

这会显着减少存储并避免更新时暂时没有默认值(或者,暂时有两个默认值)的更新问题。

您有多种选择来确保默认表中只有一行。

在 MySQL 中不能有这样的约束。

但是,如果您使用值 TRUE 和 NULL 而不是 TRUE 和 FALSE,那么它将起作用,因为 UNIQUE 列可以有多个 NULL 值。 请注意,这并不适用于所有数据库,但它适用于 MySQL。

CREATE TABLE table1(b BOOLEAN UNIQUE);

INSERT INTO table1 (b) VALUES (TRUE);   // Succeeds
INSERT INTO table1 (b) VALUES (TRUE);   // Fails: duplicate entry '1' for key 'b'

INSERT INTO table1 (b) VALUES (FALSE);  // Succeeds
INSERT INTO table1 (b) VALUES (FALSE);  // Fails: duplicate entry '0' for key 'b'

INSERT INTO table1 (b) VALUES (NULL);   // Succeeds
INSERT INTO table1 (b) VALUES (NULL);   // Succeeds!

我们如何处理 DB 上的此类问题?

在某些 DBMS 中,您可以创建部分索引。

在 PostgreSQL 中,这看起来像这样:

CREATE UNIQUE INDEX only_one_true 
  ON the_table (is_default)
  WHERE is_default

SQL Server 2008 具有非常相似的语法。

在 Oracle 上,它有点复杂,但也是可行的:

CREATE UNIQUE INDEX only_one_true 
  ON the_table (CASE 
                  WHEN is_default = 1 THEN 1
                  ELSE null
                END)

Oracle 解决方案可能适用于任何支持索引定义表达式的 DBMS。

检查触发器。 我相信它们是在 5.0.2 版中引入的。 您需要一个“插入前”触发器。 如果已经有一行 is_default=true,则引发错误。 我不知道您在并发等方面可能会遇到什么问题,但希望这足以让您开始。

我认为这不是数据库的问题,而是您的模型的问题。 我很难想出一个很好的例子来说明如何解决它,因为您没有提到您所表示的数据类型,但XXXTypeXXXConfiguration表将能够保存defaultXXXId列。

可以这样想:蓝色应该知道它是默认的,还是应该知道在给定上下文中使用时蓝色是默认的?

与尝试使用一种数据库风格的特定功能以一种对您的问题域来说不一定自然的方式来表示数据相比,更改数据建模方式通常是实现跨数据库兼容性更好的方法。

MySQL 不支持检查约束,这是使用触发器的解决方案:

create table if not exists myTable (
        id int not null auto_increment primary key,
        is_default bit not null
    ) engine=innodb;

    select 'create trigger tbi_myTable';
    drop trigger if exists tbi_myTable;
    delimiter //
    create trigger tbi_myTable 
        before insert on myTable 
        for each row
    begin
        if (select count(1) from myTable where is_default=true) > 0 && NEW.is_default then
        -- Signal is only in 5.6 and above use another way to raise an error: if less than 5.6
            SIGNAL SQLSTATE '50000' SET MESSAGE_TEXT = 'Cannot insert into myTable only one row with is_default true is allowed!';
        end if;
    END //
    delimiter ;

    insert into myTable (is_default) values (false);
    insert into myTable (is_default) values (true);
    insert into myTable (is_default) values (false);
    insert into myTable (is_default) values (false);
    -- This will generate an error
    insert into myTable (is_default) values (true);
    insert into myTable (is_default) values (false);


    select * from myTable;
    -- will give
    /*
    id  is_default
    1   false
    2   true
    3   false
    4   false
    */

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM