2017-08-31 38 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並檢查觸發器中的值。