2011-05-03 60 views
6

我正在使用Postgres,使用SERIAL作爲我的主鍵。插入一行後,我可以通過使用'RETURNING'或CURRVAL()來獲取生成的密鑰。從Posgtres批量插入返回多個SERIAL值

現在我的問題是,我想要在事務中執行批量插入並獲取所有生成的密鑰。

我得到的所有RETURNINGCURRVAL是最後生成的id,結果的其餘部分被丟棄。

我怎樣才能讓它返回所有這些?

感謝

+0

是這樣的: INSERT INTO AutoKeyEntity(名稱,描述的EntityKey)VALUES( '自動密鑰254e3c64-485e-42a4-b1cf-d2e1e629df6a','測試5/4/2011 8:59:43 AM',默認) 返回EntityKey; (CURRVAL('autokeyentity_entityKey_seq'),'Test 1 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a',0) ,(CURRVAL('autokeyentity_entityKey_seq'),'Test 2 (CURRVAL('autokeyentity_entityKey_seq'),'Test 3 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a',2) ; – Nicolas 2011-05-03 21:59:43

+0

幾個這樣一個全部在一起,所以插入和返回,一些插入使用currval,並再次相同 – Nicolas 2011-05-03 22:01:03

回答

18

您可以使用RETURNING多個值:

psql=> create table t (id serial not null, x varchar not null); 
psql=> insert into t (x) values ('a'),('b'),('c') returning id; 
id 
---- 
    1 
    2 
    3 
(3 rows) 

所以,你要更多的東西是這樣的:

INSERT INTO AutoKeyEntity (Name,Description,EntityKey) VALUES 
('AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a','Testing 5/4/2011 8:59:43 AM',DEFAULT) 
returning EntityKey; 
INSERT INTO AutoKeyEntityListed (EntityKey,Listed,ItemIndex) VALUES 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 1 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 0), 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 2 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 1), 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 3 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 2) 
returning EntityKey; 
-- etc. 

然後你必須收集返回EntityKey您交易中每條語句的值。

你可以嘗試在交易的開始和結束搶序列的當前數值並使用這些找出被使用,但that is not reliable序列值,其中:

而且,儘管多個會話保證分配 不同的序列值,當考慮所有會話時,可能會從 序列中生成值。例如,具有 緩存設定的10,會話A可能保留了1..10並且返回 nextval=1,然後會話B可能保留數值11..20並返回會話A之前 nextval=11已經產生NEXTVAL = 2。因此,如果設置爲1,那麼假設nextval的值爲 是順序生成的;與一個緩存設置大於一個你應該只假設nextval值都是不同的,而不是 它們是純粹順序生成的。另外,last_value將 反映任何會話保留的最新值,無論是否 它尚未返回nextval

所以,即使你的序列具有緩存一個值你仍然可以在你的交易不連續的序列值。但是,如果序列的緩存的值與事務中的INSERT數量相匹配,則可能會很安全,但我猜想這樣做太大而無法理解。

UPDATE:我剛剛注意到(感謝提問者的評論),有兩個表參與,有點在文本牆上迷失了。

在這種情況下,你應該能夠使用當前的INSERT:

INSERT INTO AutoKeyEntity (Name,Description,EntityKey) VALUES 
('AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a','Testing 5/4/2011 8:59:43 AM',DEFAULT) 
returning EntityKey; 
INSERT INTO AutoKeyEntityListed (EntityKey,Listed,ItemIndex) VALUES 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 1 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 0), 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 2 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 1), 
(CURRVAL('autokeyentity_entityKey_seq'),'Test 3 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 2); 
-- etc. 

而且搶在從AutoEntityKey中插入一個時間的EntityKey值之一。某些腳本可能需要處理RETURNING值。你也可以換的AutoKeyEntity及相關AutoKeyEntityListed INSERT語句中的函數,然後用INTOEntityKey值和從函數返回它:

INSERT INTO AutoKeyEntity /*...*/ RETURNING EntityKey INTO ek; 
/* AutoKeyEntityListed INSERTs ... */ 
RETURN ek; 
+0

與mutltiple值返回的事情是,我需要適當的currval輔助表,所以我不能把所有的主表插入一個單一的 – Nicolas 2011-05-03 22:35:25

+0

@Nicolas:哦,我剛剛看到'AutoKeyEntity'和'AutoKeyEntityListed'之間的區別,在「文本牆」中有點迷失。那麼爲什麼不從這些INSERT中一次一個地獲取一個'AutoKeyEntity'值,並保持原來的'AutoKeyEntityListed' INSERT? – 2011-05-03 22:45:37

+0

它工作得很好,如果我逐一去('AutoKeyEntity'和'AutoKeyEntityListed'具有currval引用,返回生成的鍵),但是我在這裏試圖完成一些性能調整做一個批量插入。可能不可能壽。 – Nicolas 2011-05-03 22:49:54

3

在應用程序中,從序列收集值:

SELECT nextval(...) FROM generate_series(1, number_of_values) n 

使用這些值創建行,並插入(使用多行插入)。它是安全的(SERIAL可以像你期望的那樣工作,不需要重用值,併發證明等),而且速度快(無需多次客戶端 - 服務器往返即可一次插入所有行)。

+0

這不是安全!它有一個非常真實的競賽條件。試試這個,打開兩個psql終端,並創建一個序列,然後從generate_series(1,1000000)運行命令select nextval('myseq');在同一時間在兩個窗口。你會得到不按順序的數字。 – 2011-05-04 16:20:53

+0

顯然不能保證數字沒有「漏洞」,但這是正常的和預期的,並且兩個併發訪問將得到不同的唯一序列號。如果目的是爲了批量插入批量序列值,那麼問題是什麼? – peufeu 2011-05-04 16:33:31

+0

哦,廢話,你說得對。我以爲你是從生成系列中抓取它們的。 DERP。抱歉。 – 2011-05-04 16:38:08

0

有三種方法可以做到這一點。使用currval(),使用返回值,或者編寫一個存儲過程來將這些方法中的任何一個封裝在一個漂亮的小毯子中,這樣可以避免在半個客戶端postgres中完成所有這些操作。

Currval method: 
begin; 
insert into table a (col1, col2) values ('val1','val2'); 
select currval('a_id_seq'); 
123 -- returned value 
-- client code creates next statement with value from select currval 
insert into table b (a_fk, col3, col4) values (123, 'val3','val4'); 
-- repeat the above as many times as needed then... 
commit; 

Returning method: 
begin; 
insert into table a (col1, col2) values ('val1','val2'), ('val1','val2'), ('val1','val2') returning a_id; -- note we inserted three rows 
123 -- return values 
124 
126 
insert into table b (a_fk, col3, col4) values (123, 'val3','val4'), (124, 'val3','val4'), (126, 'val3','val4'); 
commit; 
1

拜讀更詳細斯科特·馬洛的評論:

假設你有一個樹表與通常PARENT_ID參考它本身,而要導入一棵大樹的記錄。問題是你需要知道父母的PK值來插入孩子,所以潛在的這可能需要大量的單獨的INSERT語句。

因此,一個解決辦法是:

  • 構建的樹應用程序
  • 抓住儘可能多的序列值作爲節點插入,使用「SELECT NEXTVAL(...)FROM generate_series(1,NUMBER_OF_VALUES )N」(該值的順序並不重要)
  • 分配這些主鍵值到節點
  • 做一個大容量插入(或複製)遍歷樹結構,由於用於關係的PKS是已知
+0

請不要添加另一個答案來討論一個話題... – 2011-05-04 16:57:35

4

利用這一點,你可以預先分配連續的ID:

SELECT setval(seq, nextval(seq) + num_rows - 1, true) as stop 

它應該是一個更快的替代方案,以調用的時候nextval() gazillions。

你也可以存儲IDS在臨時表:

create temporary blah (
    id int 
) on commit drop; 

insert into table1 (...) values (...) 
returning id into blah; 

Postgres裏9。1,可以能夠使用熱膨脹係數:

with 
ids as (
insert into table1 (...) values (...) 
    returning id 
) 
insert into table2 (...) 
select ... 
from ids;