我正在使用Postgres,使用SERIAL
作为我的主键。插入行后,我可以使用“RETURNING
”或CURRVAL()
来获取生成的密钥。
现在我的问题是我想在事务中进行批量插入并获取所有生成的密钥。
我使用RETURNING
和CURRVAL
获得的是最后生成的ID,其余结果将被丢弃。
如何让它归还所有这些?
由于
答案 0 :(得分:20)
您可以将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 = 2之前nextval=11
。因此,用 缓存设置为一个可以安全地假设nextval
值 顺序生成; 缓存设置大于1的情况 应该只假设nextval
值都是不同的,而不是 它们是纯粹顺序生成的。此外,last_value
会 反映任何会话保留的最新值,无论是否 它已由nextval
返回。
因此,即使您的序列的缓存值为1,您仍然可以在事务中使用非连续的序列值。但是,如果序列的缓存值与您事务中INSERT的数量相匹配,那么您可能会安全,但我猜这会太大而无法理解。
更新:我刚刚注意到(感谢提问者的评论),涉及到两个表格,在文本墙上有点丢失。
在这种情况下,您应该能够使用当前的INSERTS:
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.
从EntityKey
上的INSERT中一次抓取AutoEntityKey
个值。可能需要某种脚本来处理RETURNING值。您还可以将AutoKeyEntity
和相关AutoKeyEntityListed
INSERT包装在函数中,然后使用INTO
获取EntityKey
值并从函数返回:
INSERT INTO AutoKeyEntity /*...*/ RETURNING EntityKey INTO ek;
/* AutoKeyEntityListed INSERTs ... */
RETURN ek;
答案 1 :(得分:4)
您可以使用以下方法预先指定连续的ID:
SELECT setval(seq, nextval(seq) + num_rows - 1, true) as stop
它应该是调用nextval()
gazillions次数的更快的替代方案。
您还可以将ID存储在临时表中:
create temporary blah (
id int
) on commit drop;
insert into table1 (...) values (...)
returning id into blah;
在postgres 9.1中,能够使用CTE:
with
ids as (
insert into table1 (...) values (...)
returning id
)
insert into table2 (...)
select ...
from ids;
答案 2 :(得分:3)
在您的应用程序中,从序列中收集值:
SELECT nextval( ... ) FROM generate_series( 1, number_of_values ) n
使用这些值创建行,然后简单地插入(使用多行插入)。这是安全的(SERIAL按照您的预期工作,不重用值,并发证明等)和快速(您一次插入所有行而无需许多客户端 - 服务器往返)。
答案 3 :(得分:1)
更详细地回复Scott Marlowe的评论:
假设您有一个树表,其中包含通常的parent_id引用,并且您想要导入一个大型记录树。问题是您需要知道插入子节点的父节点PK值,因此可能需要大量单独的INSERT语句。
所以解决方案可能是:
答案 4 :(得分:0)
有三种方法可以做到这一点。使用currval(),使用返回或写一个存储的procdure将这些方法中的任何一个包装在一个漂亮的小毯子中,这样你就不会在半个客户端的一半中完成所有操作。
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;
答案 5 :(得分:0)
执行FOR LOOP并逐个处理记录。它可能性能较低,但并发安全。
示例代码:
DO $$
DECLARE r record;
BEGIN
FOR r IN SELECT id FROM {table} WHERE {condition} LOOP
WITH idlist AS (
INSERT INTO {anotherTable} ({columns}) VALUES ({values})
RETURNING id
UPDATE {table} c SET {column} = (SELECT id FROM idlist) WHERE c.id = {table}.id;
END LOOP;
END $$;