Mysql 唯一约束允许单行组合

Mysql unique constraint allowing single row for a combination

是否可以有一个唯一的约束,使得一个特定的列只有一次值?

例如

-----------------------    
name | price | default
-----------------------
XYZ  |  20   | TRUE
-----------------------
XYZ  |  30   | FALSE
-----------------------
XYZ  |  40   | FALSE
-----------------------
ABC  | 50    | FALSE
-----------------------

所以在上面的 table 中,对于特定的 name 值,default 值只能为 TRUE 一次。并将对名称和价格列有唯一约束。

这可能吗?

我用一个奇怪的方法解决了这个问题,使用 TIMESTAMP 作为标志。

它被用作“已删除”标志。

如果 IS NULL,则寄存器不会被删除 - 相反,如果被删除 (<> NULL),唯一键永远不会发生冲突,避免未标记为已删除寄存器的重复寄存器。

例如你的情况:

你永远不会有 2 行 默认值:NULL,但你可以有 NTIMESTAMP 他们排除了时间。

您可以创建一个触发器来检查是否已经存在具有 'TRUE' 值的字段,如果存在则采取行动。

请注意,您无法轻易 "reject" 更新。 (参见例如:How to abort INSERT operation in MySql trigger?)。

例如,您可以将其插入为 false,然后通过设置标志以某种方式保存您的错误。

对所有三列设置一个简单的唯一约束是行不通的,因为它会允许以下情况:

name | price | default
-----------------------
XYZ  |  20   | TRUE
XYZ  |  30   | TRUE
XYZ  |  40   | FALSE
ABC  |  50   | FALSE

在其他 SQL 引擎中,您可以创建一个检查约束,以确保对于每组 name 行,具有 default = TRUE 的行数 <= 1 . 但是, MySQL 不支持强制检查约束。

在 MySQL 中,您可以通过 insertupdate 触发器来实现:

CREATE TRIGGER `before_insert` BEFORE INSERT ON `tableName`
FOR EACH ROW
BEGIN

   DECLARE @sum INT

   SELECT @sum = SUM(CASE WHEN [default] = TRUE THEN 1 ELSE 0 END)
   FROM tableName
   WHERE [name] = new.[name]

   IF (@sum = 0 OR new.default = FALSE)
   BEGIN
      INSERT INTO tableName (name, price, default)
      VALUES (new.[name], new.[price], new.[defaul]t)

   END
END

update.

也类似

这是一个可能适合您需要的解决方法。考虑以下 table 定义和唯一约束:

CREATE TABLE prices
(
    id int NOT NULL AUTO_INCREMENT,
    name varchar(255) NOT NULL,
    price int,
    default varchar(255)
)

ALTER TABLE prices ADD UNIQUE idx (id, name, default);

在您的应用程序层中,当您 INSERT 记录 defaultFALSE 时,始终保留 idNULL。这将导致 MySQL 始终为 id 添加唯一值,因此可能会输入 namedefault 的重复值(即 default 值对于给定的 name).

FALSE 可能会出现多次

但是,当您 INSERT 一条记录 defaultTRUE 时,您应该始终使用 相同的 id价值。这将确保给定的 name 只能作为 TRUE.

出现一次

一种 normal 方法是提取一个单独的 table 来保存默认价格:

CREATE TABLE price (
    name VARCHAR(255),
    price INT,
    PRIMARY KEY (name, price)
) ;

CREATE TABLE defaultPrice (
    name VARCHAR(255),
    price INT,
    PRIMARY KEY (name),
    FOREIGN KEY(name, price) REFERENCES price(name, price)
);

大多数人会建议引入代理键:

CREATE TABLE item (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    UNIQUE(name)
);

CREATE TABLE price (
    itemId INT,
    price INT,
    PRIMARY KEY (itemId, price),
    FOREIGN KEY (itemId) REFERENCES item (id)
) ;

CREATE TABLE defaultPrice (
    itemId INT,
    price INT,
    PRIMARY KEY (itemId),
    FOREIGN KEY (itemId, price) REFERENCES price (itemId, price)
);