2017-08-31 37 views
0

如下表:INSERT和检测到的唯一约束错误

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号/ sourcesId/321不存在,请插入记录。
  • 如果123/321的id/sourcesId确实存在,并且name等于'name',则忽略。
  • 如果123/321的id/sourcesId确实存在,并且name不等于'name',则抛出异常。

sourcesId先前已被验证为现有。

这是可能的,如果是的话如何?

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

+0

基于唯一性约束,您的第二个要求将因数据库异常而失败。另外,我认为你可能需要一个唯一的名字来颠倒你的逻辑。我认为你应该忽略并且在2和3之间抛出。 –

+0

@RossBush 2不应该是一个错误,因为没有任何改变。 3应该是一个错误,因为改变了一些东西我的意图是,然后在另一个包含'id.devices_used_by_resources'的表中执行插入操作,并且需要确保它存在并且尚未更改。 – user1032531

+0

我误解了您的意图。但是,它并不清楚。为什么不将id字段添加到nameSourceUniqueInx唯一约束中。这样可以保证表中所有的id/sourceId/name组合都是唯一的。 –

回答

1

原样使用该语句是不可能的。违反唯一密钥会引发错误。您必须找到该错误,或​​者使用insert ignore(不允许您对第三种情况作出反应)或使用on duplicate key

如果您的条件符合,您可以使用on duplicate key故意抛出错误。不幸的是,错误消息不会与违规直接相关,它只会是“任何”异常(反常描述您的情况的异常不存在)。在update -part中,检查名称是否不同。如果没有改变,你什么都不做(“忽略”),如果改变了,你可以设置一个无效的值为例如抛出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 

(name, sourcedId)有第二唯一索引,这也将引发on duplicate key。你没有specifiy如果你插入一行违反本应该发生什么,所以该语句会忽略它(违反(name, sourcedId)不会改名字,所以不会引发任何异常):

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没有插入,但我想这不会发生的时候,也许你可以发现,覆盖了太多的消息 - 或者你做一些更复杂的通信在upsert和触发器之间,例如将idsourcesId设置为-812677并检查触发器中的值。