Mysql 插入并检测唯一约束错误

Mysql 插入并检测唯一约束错误,mysql,sql,exception,mariadb,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, s

鉴于下表:

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不存在,请插入记录
  • 如果123/321的id/sourcesId确实存在且名称等于“名称”,则忽略
  • 如果123/321的id/sourcesId确实存在且名称不等于“名称”,则引发异常
sourcesId
以前已验证为存在

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


PS.是否需要索引sourcesIdInx?如果需要,原因是什么?

无法按原样使用该语句。违反唯一密钥将引发错误。您必须使用
insert ignore
(这不允许您对第三种情况做出反应)或重复键上的
捕捉该错误

您可以在重复键上使用
,如果您的条件得到满足,该键将故意抛出错误。不幸的是,errormessage不会与违规直接相关,它只是“任何”异常(描述您的情况的异常无论如何都不存在)。在更新部分中,检查名称是否不同。如果未更改,则不执行任何操作(“忽略”),如果更改,则可以设置无效值,例如抛出一个
非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
您在
(name,sourcedId)
上有第二个唯一索引,它还将在重复键上触发
。您没有指定如果插入违反此规则的行会发生什么,因此语句将忽略它(违反
(name,sourcedId)
不会更改名称,因此不会引发异常):

如果也要引发异常,则可以比较所有值,而不仅仅是名称,因此不同的
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 ;  

如果您在没有插入的情况下使用资源集id=null使用的
更新设备\u,它也会抛出该错误,但我想这不会经常发生,或者您也可以找到一条包含该错误的消息-或者您在upsert和触发器之间进行更复杂的通信,例如,将
id
sourcesId
设置为
-812677
,并在触发器中检查该值。

无法按原样使用该语句。违反唯一密钥将引发错误。您必须使用
insert ignore
(这不允许您对第三种情况做出反应)或重复键上的
捕捉该错误

您可以在重复键上使用
,如果您的条件得到满足,该键将故意抛出错误。不幸的是,errormessage不会与违规直接相关,它只是“任何”异常(描述您的情况的异常无论如何都不存在)。在更新部分中,检查名称是否不同。如果未更改,则不执行任何操作(“忽略”),如果更改,则可以设置无效值,例如抛出一个
非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
您在
(name,sourcedId)
上有第二个唯一索引,它还将在重复键上触发
。您没有指定如果插入违反此规则的行会发生什么,因此语句将忽略它(违反
(name,sourcedId)
不会更改名称,因此不会引发异常):

如果也要引发异常,则可以比较所有值,而不仅仅是名称,因此不同的
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 ;  

如果您在没有插入的情况下使用资源集id=null使用的
更新设备\u,它也会抛出该错误,但我想这不会经常发生,或者您也可以找到一条包含该错误的消息-或者您在upsert和触发器之间进行更复杂的通信,例如,将
id
sourcesId
设置为
-812677
,并在触发器中检查该值。

基于唯一约束,您的第二个要求将失败,并出现数据库异常。此外,我认为您可能需要一个唯一的名称,这与您的逻辑相反。我认为你应该在2和3之间切换忽略和抛出。@RossBush 2不应该是一个错误,因为没有任何更改。3应该是一个错误,因为某些内容已更改。我的目的是在另一个表中执行插入操作,该表包含
id.devices\u被\u resources
使用,并且需要确保它存在并且没有被更改。我误解了您的意图。然而,目前还不清楚。为什么不将id字段添加到nameSourceUniqueInx唯一约束中呢。这样可以保证表中的所有id/sourceId/name组合都是唯一的。@RossBush,因为对于给定的sourcesId,无论id值如何,名称都必须是唯一的。根据唯一性约束,第二个要求将失败,并出现数据库异常。此外,我认为您可能需要一个唯一的名称,这与您的逻辑相反。我认为你应该在2和3之间切换忽略和抛出。@RossBush 2不应该是一个错误,因为没有任何更改。3应该是一个错误,因为某些内容已更改。我的目的是在另一个表中执行insert,该表包含资源所使用的
id.devices\u,并且需要确保它存在于