12

我有一个带有 SERIAL 主键的表,还有一个 ltree 列,我希望它的值是这些主键的串联。例如

id | path
----------
1    1
2    1.2
3    1.2.3
4    1.4
5    1.5

我很好奇是否有办法在一个查询中进行这样的插入,例如

INSERT INTO foo (id, ltree) VALUES (DEFAULT, THIS.id::text)

我可能在这里过分了,并试图在一个查询中完成我应该在两个查询中执行的操作(在事务中分组)。

4

2 回答 2

13

您可以使用 CTE 从序列中检索一次值并重复使用它:

WITH cte AS (
   SELECT nextval('foo_id_seq') AS id
   )
INSERT INTO foo (id, ltree)
SELECT id, '1.' || id
FROM   cte;

带有数据修改命令的CTE需要 Postgres 9.1 或更高版本。

如果您不确定序列的名称,请 pg_get_serial_sequence()改用:

WITH i AS (
   SELECT nextval(pg_get_serial_sequence('foo', 'id')) AS id
   )
INSERT INTO foo (id, ltree)
SELECT id, '1.' || id
FROM   i;

如果表名“foo”在数据库中的所有模式中可能不是唯一的,请对其进行模式限定。如果任何名称的拼写不标准,则必须双引号:

pg_get_serial_sequence('"My_odd_Schema".foo', 'id')



快速测试表明@Mark 的想法可能有效:lastval()

INSERT INTO foo (ltree) VALUES ('1.' || lastval());
  • 您可以忽略id查询,该serial列将自动分配。没什么区别。

  • 行之间不应该有竞争条件。我引用手册

currval

nextval返回当前会话中此序列最近获得的值。(如果nextval在此会话中从未为此序列调用过,则会报告错误。)因为这将返回一个会话本地值,所以它给出了一个可预测的答案,无论nextval自当前会话以来其他会话是否已执行。

此功能需要USAGESELECT对序列具有特权。

lastval

nextval返回当前会话中最近返回的值。此函数与 相同currval,不同之处在于它不是将序列名称作为参数,而是引用nextval当前会话中最近应用的序列。lastval如果nextval当前会话中尚未调用,则调用是错误的。

此功能需要USAGESELECT对最后使用的序列具有特权。

大胆强调我的。

但是,正如@Bernard 评论的那样,它毕竟可能会失败:不能保证在调用填充第二列之前nextval()填充默认值(并在过程中调用)。所以坚持第一个解决方案并确保。 lastval()ltreenextval()

于 2012-09-15T00:08:07.533 回答
4

这在我的测试中有效:

INSERT INTO foo (id, ltree) VALUES (DEFAULT, (SELECT last_value from foo_id_seq));

如果两个 INSERT 同时发生,我认为那里存在竞争条件,因为这引用了最后一个序列值,而不是当前行。我个人更倾向于这样做(伪代码):

my $id = SELECT nextval('foo_id_seq');
INSERT INTO foo (id, ltree) VALUES ($id, '$id');
于 2012-09-14T23:38:12.360 回答