我是下表:
DROP SEQUENCE IF EXISTS CATEGORY_SEQ CASCADE;
CREATE SEQUENCE CATEGORY_SEQ START 1;
DROP TABLE IF EXISTS CATEGORY CASCADE;
CREATE TABLE CATEGORY (
ID BIGINT NOT NULL DEFAULT nextval('CATEGORY_SEQ'),
NAME CHARACTER VARYING(255) NOT NULL,
PARENT_ID BIGINT
);
ALTER TABLE CATEGORY
ADD CONSTRAINT CATEGORY_PK PRIMARY KEY (ID);
ALTER TABLE CATEGORY
ADD CONSTRAINT CATEGORY_SELF_FK FOREIGN KEY (PARENT_ID) REFERENCES CATEGORY (ID);
现在我需要插入数据。所以我从父母开始:
INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1');
现在我需要刚刚插入的父项的ID来添加子项:
INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES ('CHILDREN_1_1', <what_goes_here>);
INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES ('CHILDREN_1_2', <what_goes_here>);
如何获取并存储父级的ID以便以后在后续插入中使用它?
答案 0 :(得分:2)
您可以使用带有returning
子句的数据修改CTE:
with parent_cat (parent_id) as (
INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1')
returning id
)
INSERT INTO CATEGORY (NAME, PARENT_ID)
VALUES
('CHILDREN_1_1', (select parent_id from parent_cat) ),
('CHILDREN_1_2', (select parent_id from parent_cat) );
答案 1 :(得分:0)
答案是使用RETURNING
和WITH
WITH inserted AS (
INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1')
RETURNING id
) INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES
('CHILD_1_1', (SELECT inserted.id FROM inserted)),
('CHILD_2_1', (SELECT inserted.id FROM inserted));
答案 2 :(得分:-1)
(tl;dr
:转到选项3:使用RETURNING INSERT)
回想一下,在postgresql中没有&#34; id&#34;表格的概念,只是序列(通常但不一定用作代理主键的默认值,SERIAL伪类型)。
如果您有兴趣获取新插入行的ID,可以采用以下几种方法:
选项1:CURRVAL(<sequence name>);
。
例如:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval('persons_id_seq');
序列的名称必须是已知的,它确实是任意的;在此示例中,我们假设表persons
具有使用id
伪类型创建的SERIAL
列。为了避免依赖于此并感觉更干净,您可以使用pg_get_serial_sequence
:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval(pg_get_serial_sequence('persons','id'));
警告:currval()
仅适用于INSERT
(已执行nextval()
),在同一会话中。
选项2:LASTVAL();
这与前一个类似,只是您不需要指定序列号:它会查找最近修改的序列(总是在您的会话中,与上面相同的警告)。
CURRVAL
和LASTVAL
都是完全并发安全的。 PG中序列的行为被设计为不同的会话不会干扰,因此不存在竞争条件的风险(如果另一个会话在我的INSERT和SELECT之间插入另一行,我仍然得到正确的值)。
然而他们确实存在一个微妙的潜在问题。如果数据库有一些TRIGGER(或RULE),那么在插入persons
表时,会在其他表中进行一些额外的插入......那么LASTVAL
可能会给我们错误的值。问题甚至可能发生在CURRVAL
,如果额外的插入是在同一个persons
表中完成的(这通常不太常见,但风险仍然存在)。
选项3:INSERT
与RETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
这是获取ID的最干净,最有效和最安全的方式。它没有任何前一个风险。
缺点?几乎没有:您可能需要修改调用INSERT语句的方式(在最坏的情况下,可能您的API或DB层不期望INSERT返回值);它不是标准的SQL(谁在乎);它自Postgresql 8.2(2006年12月......)
以来可用结论:如果可以,请选择选项3.在其他地方,请选择1。
注意:如果您打算获取最后一次全局插入的ID (不一定在您的会话中),则所有这些方法都无用。为此,您必须使用select max(id) from table
(当然,这不会读取其他事务中未提交的插入内容)。