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,但你可以有 N 和 TIMESTAMP 他们排除了时间。
您可以创建一个触发器来检查是否已经存在具有 '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 中,您可以通过 insert
和 update
触发器来实现:
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
记录 default
为 FALSE
时,始终保留 id
列 NULL
。这将导致 MySQL 始终为 id
添加唯一值,因此可能会输入 name
和 default
的重复值(即 default
值对于给定的 name
).
,FALSE
可能会出现多次
但是,当您 INSERT
一条记录 default
为 TRUE
时,您应该始终使用 相同的 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)
);
是否可以有一个唯一的约束,使得一个特定的列只有一次值?
例如
-----------------------
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,但你可以有 N 和 TIMESTAMP 他们排除了时间。
您可以创建一个触发器来检查是否已经存在具有 '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 中,您可以通过 insert
和 update
触发器来实现:
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
记录 default
为 FALSE
时,始终保留 id
列 NULL
。这将导致 MySQL 始终为 id
添加唯一值,因此可能会输入 name
和 default
的重复值(即 default
值对于给定的 name
).
FALSE
可能会出现多次
但是,当您 INSERT
一条记录 default
为 TRUE
时,您应该始终使用 相同的 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)
);