另一个我更喜欢的想法是这样的:
- 创建一个函数,从时间戳和发票号生成您的id,
- 创建一个常规表格,其中包含:
foo_id
: 简单的序列(递增整数)
ts_created
字段。
- 在需要时查询生成您的发票id,
下面是它的样子,首先我们创建一个函数,从
bigint
和
timestamp
生成
acme_id
。
CREATE FUNCTION acme_id( seqint bigint, seqts timestamp with time zone )
RETURNS char(10)
AS $$
SELECT format(
'%04s%02s%04s',
EXTRACT(year FROM seqts),
EXTRACT(month from seqts),
to_char(seqint, 'fm0000')
);
$$ LANGUAGE SQL
IMMUTABLE;
然后我们创建一个表格。
CREATE TABLE foo (
foo_id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
data text,
ts_created timestamp with time zone DEFAULT NOW()
);
CREATE INDEX ON foo(ts_created, foo_id);
现在,您可以使用简单的
窗函数生成您想要的内容。
SELECT acme_id(
ROW_NUMBER() OVER (
PARTITION BY date_trunc('MONTH', ts_created)
ORDER BY ts_created
),
ts_created
), *
FROM foo;
我会构建系统,使其在内部使用
foo_id
。只要您不从
foo
中删除,就始终能够从行中呈现相同的发票 ID,只是无需存储它。
甚至可以使用 [materialized] 视图缓存呈现和发票 ID。
CREATE MATERIALIZED VIEW acme_invoice_view
AS
SELECT acme_id(
ROW_NUMBER() OVER (
PARTITION BY date_trunc('MONTH', ts_created)
ORDER BY ts_created
),
ts_created
), *
FROM foo;
;
SELECT * FROM acme_invoice_view;
acme_id | foo_id | insert_date | data
2021100001 | 1 | 2021-10-12 | bar
(1 row)
请记住此方法的缺点:
- 发票表中的行永远不能被删除(你可以添加一个布尔值来停用它们),
foo_id
和ts_created
应该是不可变的(永远不会更新),否则可能会得到一个新的发票ID。代理键(foo_id
无论如何都不应该更改)。
这种方法的好处:
- 存储发票非常有用的实际时间戳
- 真正的代理键(我会在所有上下文中使用它,而不是发票ID),简化了与其他表的链接,并且更有效率和快速。
- 发票日期的单一真相来源
- 很容易发出新的发票-id方案,甚至将其映射到旧方案中。