mysql - INSERT 并检测唯一约束错误

标签 mysql sql exception mariadb

给出下表:

CREATE TABLE IF NOT EXISTS devices_used_by_resource (
  id INT NOT NULL,
  sourcesId INT NOT NULL,
  name VARCHAR(45) NOT NULL,
  PRIMARY KEY (id, sourcesId),
  INDEX sourcesIdInx (sourcesId ASC),
  UNIQUE INDEX nameSourceUniqueInx (name ASC, sourcesId ASC),
  CONSTRAINT existingSourcesId
    FOREIGN KEY (sourcesId)
    REFERENCES sources (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

我希望进行以下查询:

INSERT INTO devices_used_by_resource(id,sourcesId,name) VALUES(123,321,'the name');

响应为:

  • 如果123/321的id/sourcesId不存在,则插入记录。
  • 如果 id/sourcesId 123/321 确实存在且 name 等于“名称”,则忽略。
  • 如果 id/sourcesId 123/321 确实存在,并且 name 不等于“名称”,则抛出异常。

sourcesId 之前已被验证为存在。

这可能吗?如果可能的话,如何实现?

PS。是否需要索引sourcesIdInx,如果需要,为什么?

最佳答案

不可能按原样使用该语句。违反唯一键将引发错误。您必须捕获该错误,要么使用insertignore(这不允许您对第三个条件使用react),要么使用on重复键

您可以使用重复键,如果满足您的条件,它会故意抛出错误。不幸的是,错误消息不会与违规直接相关,它只是“任何”异常(描述您的情况的异常无论如何都不存在)。在update部分,检查名称是否不同。如果它没有改变,你什么都不做(“忽略”),如果它改变了,你可以设置一个无效的值,例如抛出not null-错误:

INSERT INTO devices_used_by_resource(id,sourcesId,name) 
VALUES(123,321,'the name')
on duplicate key update id = if(name = values(name), id, null);

> 1 row(s) affected

INSERT INTO devices_used_by_resource(id,sourcesId,name) 
VALUES(123,321,'the name')
on duplicate key update id = if(name = values(name), id, null);

> 0 row(s) affected

INSERT INTO devices_used_by_resource(id,sourcesId,name) 
VALUES(123,321,'the name1')
on duplicate key update id = if(name = values(name), id, null);

> Error Code: 1048. Column 'id' cannot be null

您在(名称,sourcedId)上有第二个唯一索引,它也会在重复键上触发。您没有指定如果插入违反此规定的行会发生什么,因此该语句将忽略它(违反 (name, sourcedId) 不会更改名称,因此也不异常(exception)抛出):

INSERT INTO devices_used_by_resource(id,sourcesId,name) 
VALUES(1,321,'the name')
on duplicate key update id = if(name = values(name), id, null);

> 0 row(s) affected

如果您也想引发异常,您可以比较所有值而不仅仅是名称,因此不同的 id 也会引发错误:

INSERT INTO devices_used_by_resource(id,sourcesId,name) 
VALUES(123,321,'the name1')
on duplicate key update 
id = if(id = values(id) and sourcesId = values(sourcesId) 
        and name = values(name), id, null);

> Error Code: 1048. Column 'id' cannot be null

异常消息显然对错误不是很清楚,因为它引发了一个不相关的错误。如果您只是想捕获该异常并知道它的实际含义,那就没问题了。为了使其更时尚,您可以添加一个触发器,该触发器使用 id=null 作为指示器来抛出自定义消息,例如

delimiter $$
create trigger trbu_devices_used_by_resource 
  before update on devices_used_by_resource
for each row
begin
  if new.id is null then 
    SIGNAL SQLSTATE '45000' 
    SET message_text = 'Inserted duplicate entry with different attributes!';
  end if;
end $$  
delimiter ;  

如果您在没有插入的情况下使用update devices_used_by_resource set id = null,它也会抛出该错误,但我想这种情况不会经常发生,或者也许您也可以找到一条涵盖该错误的消息- 或者您在更新插入和触发器之间进行一些更复杂的通信,例如将 idsourcesId 设置为 -812677 并在触发器中检查该值。

关于mysql - INSERT 并检测唯一约束错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45985254/

相关文章:

mysql - 如何使用 IF 条件连接 5 个表

java - 在 Controller Spring MVC中控制异常流

sql - SQL 查询中 NOT IN 和 NOT EQUALS 不同行为的区别

php - 用PHP搜索查询

c# - 我可以重载 throw 关键字吗?

python - 捕获 asyncore 类中的异常

mysql - 这里有一些基本的东西 : Invalid Group function

mysql - 选择列 MYSQL 中的所有不同单词

java - MySQL CASE使用如何删除重复的空列

sql - 第二个检查约束是什么意思?