另一个我更喜欢的想法是,
- 创建一个函数,根据时间戳和发票号生成您的 ID,
- 创建常规表,
-
foo_id:简单序列(递增 int)
-
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);
现在您可以使用简单的window function. 生成您要查找的内容
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,您就不必存储它。
您甚至可以使用 [物化] 视图缓存渲染和发票 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 方案,甚至可以将其映射到旧方案。