2011-09-27 98 views
12

我正試圖在表中創建一個新行。表中有兩個約束 - 一個在關鍵字段(DB_ID)上,另一個約束一個值爲幾個字段ENV之一。當我做一個插入,我不包括鍵字段,因爲我試圖插入的領域之一,但我得到這個錯誤:插入期間唯一約束違規:爲什麼? (Oracle)

unique constraint (N390.PK_DB_ID) violated 

下面是導致該錯誤的SQL:

insert into cmdb_db 
    (narrative_name, db_name, db_type, schema, node, env, server_id, state, path) 
values 
    ('Test Database', 'DB', 'TYPE', 'SCH', '', 'SB01', 381, 'TEST', '') 

我唯一能夠提出的是Oracle可能試圖分配已經使用的DB_ID,如果手動插入行。這個數據庫中的數據以某種方式從生產數據庫中恢復/移動,但我沒有關於如何完成的細節。

有什麼想法?

回答

34

據推測,因爲你沒有爲DB_ID列提供了一個值,這個值是由在表上定義INSERT觸發器前行級填充。該觸發器大概是從序列中選擇值。

由於數據是從生產數據庫中移出的(大概是最近),我的下注是當數據被複制時,序列也沒有被修改。我猜測序列生成的值遠遠低於當前導致錯誤的表中最大的DB_ID

你可以通過查看觸發以確定正在使用哪個序列,並做了

SELECT <<sequence name>>.nextval 
    FROM dual 

和比較,爲

SELECT MAX(db_id) 
    FROM cmdb_db 

如果證實這一懷疑,因爲我懷疑,序列正在生成數據庫中已存在的值,您可以遞增序列直到它生成未使用的值,或者可以更改它以將INCREMENT設置爲非常大的值,獲取nextval一次,並設置INCREMENT回到1。

+9

+1明智的猜測 – APC

1

看起來您沒有爲主鍵字段DB_ID提供值。如果這是主鍵,則必須爲該列提供唯一值。不提供它的唯一方法是創建一個數據庫觸發器,該數據庫觸發器在插入時將提供一個值,最有可能從一個序列派生。

如果這是來自另一個數據庫的恢復,並且此新實例上有一個序列,則它可能試圖重用一個值。如果舊數據具有從1到1000的唯一鍵,並且當前序列是500,則它將生成已存在的值。如果該表存在一個序列並試圖使用它,則需要將表中的值與序列的當前值進行協調。

您可以使用SEQUENCE_NAME.CURRVAL看到序列的當前值(如果存在的話,當然)

+0

我對數據庫的信息表明,插入新行時應該自動生成密鑰 - 這就是爲什麼我不提供DB_ID值的原因。此外,這種技術也適用於數據庫中其他類似的表 - 它們具有自動生成的ID鍵。 – Sean

+0

您是否能夠查詢數據字典以查看新環境中是否存在必要的觸發器/序列?也許並非所有事情都是從舊環境中帶來的。 –

1

你的錯誤看起來你是在複製你的數據庫已經存在的主鍵。你應該修改你的sql代碼來使用IDENTITY關鍵字來實現自己的主鍵。

CREATE TABLE [DB] (
    [DBId] bigint NOT NULL IDENTITY, 
    ... 

    CONSTRAINT [DB_PK] PRIMARY KEY ([DB] ASC), 

); 
+1

IDENTITY在Oracle中不是有效的關鍵字。 –

+0

對於Oracle而言,這是一條無效聲明 –

+1

先生,您是正確的。我的解決方案適用於SQL,而不適用於Oracle。如果你想在Oracle中複製SQL的Identity,你可能想使用Sequences [鏈接](http://www.techonthenet.com/oracle/sequences.php)@Justin_Cave在他的答案中有一個很好的實現。 –