Postgres - 顺序计数数字字段(意大利语计数)

Postgres - sequential counting numeric field (Italian counting)

我正在构建一个生成运输单据的系统。 table 具有以下字段:

ID - Numeric And primary key
Reference_Client - Numeric reference to the customer
Date- Current Date
Code

在意大利,每份运输单据都有序号。 Code需要从0开始,每增加一个Date.

如何设置Code获取自动递增的数字?

您需要一个在每个新的一天 1 重新开始的序列。

时区

所以第一个任务是定义新的一天的含义。对于任何给定时刻,世界各地的日期因时区而异。例如,巴黎的午夜过后几分钟在蒙特利尔仍然是“昨天”。

因此,您需要选择一个“今天”对您的应用程序有意义的时区。也许这意味着 UTC。例如,Stack Overflow 每天在此站点上显示您的 activity 时使用 UTC。因此,对于我们这些北美西海岸的人来说,Stack Overflow 上新的“一天”从下午 4 点或 5 点开始。

如果不是 UTC,请了解您的真实时区。切勿使用媒体上常见的 3-4 个字母缩写,例如 ESTIST。这些既不是标准化的也不是唯一的!使用proper time zone names,由continent/region组成,如America/Montreal

使用一个好的日期时间库。不幸的是,这种情况很少见。对于 Java,使用内置于 Java 6 和 7 中的 java.time 框架(具有可用的后向端口)。请务必使该库中的 tz database 保持最新,以适应政客们喜欢进行的过于频繁的更改。对日期时间支持良好的数据库可能有自己的 tz 数据库副本,可能需要更新。

顺便说一下,这一天 而不是 总是从 00:00:00.0 开始。在一些时区,Daylight Saving Time (DST) 转换发生在午夜。

序列

现在您需要生成一个数字序列。通常有两种方法可以做到这一点:

  • 在您的数据库中使用序列生成器。
  • 滚动你自己的序列生成器。

如果可能,最好使用数据库服务器的序列生成器。管理序列比您想象的要棘手。 Concurrency is always a tough nut to manage correctly. You have to make sure the sequence does not hand out the same number twice. And when transactions 回滚,您可能需要处理获得的序列号,因为另一个并发事务可能已经移动到下一个序列号。或者您 do 想要回收这些未实际使用的数字(您的应用程序需要决定的策略),这意味着存储这些数字以供下次使用,而不是生成连续的数字。

我建议查看您的数据库的功能。如果您的数据库提供了一个复杂可靠的序列生成器,并且您的数据库提供了一种服务器端编程语言,例如 PL/pgSQL in Postgres,那么我建议编写一个方法供您的应用程序调用以生成每个日期的序列。

创建一个名称包含日期的序列。由您的数据库管理的序列对象的名称可能类似于 transport-seq-2016-05-12。当你的方法被调用时,它会确定今天的日期,组合字符串“transport-seq-”加上一个表示日期的字符串。我建议坚持使用标准 ISO 8601 格式,YYYY-MM-DD 用于该字符串。在数据库的元数据中查询带有该名称的序列对象,或者尝试使用该序列并捕获不存在的序列所发生的错误。

当找不到序列时,这意味着日期已经过去了。您有两个选择:

  • 用包含今天日期的新字符串重命名旧序列,并将序列重置为 0(或 1)。
  • 新建一个序列对象,默认为0(或1)。让旧的序列对象在闲置中慢慢积累。

这两种方法都存在并发问题。请记住,确定今天日期的代码行是不同的,并且与查找现有序列的代码分开(非原子地)运行。同样,寻找现有序列的代码与重命名序列或创建新序列的代码分开运行(非原子地)。

重命名序列

如果重命名,您需要确保您的数据库能够以事务安全的方式处理序列重命名。并且您需要处理这样一种可能性,即一个线程(线程 A)即将重命名和重置序列,被挂起,另一个线程(线程 B)自己进行重命名和重置,然后线程 A 继续它是再次重命名和重置的方式。你的代码需要防止或解决这个 A-B-A 问题;可能这意味着处理当 A 尝试重命名不再存在的旧名称序列时发生的错误。并且可能还有其他我没有想到的并发问题。一方面,序列命名需要由数据库以原子方式执行,在序列的任何其他使用之后完成,并在序列的任何其他使用之前完成。此外,序列重命名和重置是两个不同的操作,不会是原子的,因此您需要某种保护,可能是 semaphore.

创建序列

创建新序列可能更安全,并发问题更少。但我怀疑您仍然需要围绕创建新序列的某种信号量保护,在与上述类似的 A-B-A 问题中。线程 A 可能已经确定需要一个新序列,但随后被中断。同时线程 B 运行,也确定是否需要新序列,创建序列,并且线程 A 继续运行只会遇到错误,因为序列已经存在。此类并发问题可以妥善处理,您只需要彻底处理并避免生产中的运行时故障即可。而且,还有一件苦差事:您可能希望偶尔执行清理操作以删除旧的序列对象。

滚动你自己的序列

如果您的数据库缺少功能强大的序列生成器,请创建您自己的序列生成器。创建一个名为 transport_sequence_ 的 table。这个 table 可以只有一行或多行,遵循上面关于重命名或创建新行的相同逻辑。

无论哪种方式,我建议在数据库中实现一个服务器端方法,由应用调用,而不是应用试图管理序列。

这个table有两列,一个是日期类型,一个是整数类型。整数表示最后使用的数字(或下一个使用的数字,具体取决于您的喜好)。如果重命名,请在新的一天结束时更改日期。或者使用新日期创建一个新行。使用数据库的事务和并发功能来增加行的整数字段中的数字。说起来容易做起来难。

如果您制定了重复使用任何已取消号码以避免在序列中留下漏洞的政策,则添加另一个相关子项 table,为每个重复使用的号码添加一行。当行号被分发给另一个事务时,行被删除。