Postgres动态创建序列

alo*_*ras 2 database postgresql database-design

我正在编写一个有多个用户的应用程序,用户可以在应用程序内上传报告。

目前,我有一个“报告”表,其中包含所有已提交的报告,该表具有一个“ id”字段,该字段是表上的串行主键。

我指定的要求是,用户需要能够指定前缀和数字,以便他们的报告从此开始计数。例如,用户应该可以说他们的报告始于ABC-100,然后下一个报告是ABC-101,ABC-102,依此类推。

我想实现此目的的方式是,当用户创建一个帐户时,他可以指定前缀和起始编号,然后我将创建一个postgres序列,其中包含指定的前缀名称和minValue用户希望报告从此开始。

然后,当用户提交新报告时,我可以将report_number标记为nextval(prefix_sequence)。从理论上讲,这是可行的,但是我对postgres来说还很陌生,我想就这是对序列的良好使用还是更好的方法提供一些建议和反馈。

Cra*_*ger 6

在这个区域中,您可能不需要序列的主要好处-可以将它们同时用于多个事务。您可能也不想让不利的一面是序列中的空白是正常的。获得输出是很正常的,例如1, 2, 4, 7, 8, 12, ...您有并发事务,回滚等。

在这种情况下,您最好选择柜台。用户创建帐户时,请在account_sequences表格中创建一行(account_id, counter)。难道不是将其存储在账户的主表,因为你会锁定并更新了很多,而且要最大限度地减少VACUUM工作量。

例如

CREATE TABLE account_sequences
(
    account_id integer PRIMARY KEY REFERENCES account(id),
    counter integer NOT NULL DEFAULT 1,
);
Run Code Online (Sandbox Code Playgroud)

现在编写一个简单的LANGUAGE SQL函数,例如

CREATE OR REPLACE FUNCTION account_get_next_id(integer)
RETURNS integer VOLATILE LANGUAGE sql AS
$$
UPDATE account_sequences
SET counter = counter + 1
WHERE account_id = $1
RETURNING counter
$$;
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用它代替nextval。这将起作用,因为UPDATE与相关account_sequences行相关的每个事务都会对其持有的行进行锁定,直到提交或回滚为止。其他尝试获取同一帐户ID的交易将等待其完成。

有关更多信息,请搜索“ postgresql无缝序列”。

如果需要,您也可以使SQL函数也提取前缀,使用format,将其与生成的值连接起来,然后返回text结果。如果将prefix text NOT NULL列放入account_sequences表中,这将更加容易,因此您可以执行以下操作:

CREATE OR REPLACE FUNCTION account_get_next_id(integer)
RETURNS text VOLATILE LANGUAGE sql AS
$$
UPDATE account_sequences
SET counter = counter + 1
WHERE account_id = $1
RETURNING format('%s%s', prefix, counter)
$$;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,千万不能拿使用带有子查询的朴素的办法SELECT max(id) ...。这完全是并发不安全的,如果同时运行多个事务,它将产生错误的结果或错误。而且它很慢。