如何根据行创建日期在PostgreSQL中创建自定义序列?

roc*_*hus 7 sql postgresql ruby-on-rails

我正在创建一个Rails应用程序,它将取代我的雇主的(非常旧的)遗留订单管理应用程序.新系统的规格之一是订单编号系统保持不变.现在,我们的订单号格式如下:

  • 前四位是当前年份
  • 接下来的两位数是当月
  • 下一个(和最后一个)四位数字是一个计数器,每当在该月份下订单时,该计数器会递增1.

例如,2014年6月的第一个订单的订货号为2014060001.下一个订单的订货号为2014060002,依此类推.

此订单号需要是Orders表中的主要ID.看来我需要为PostgreSQL设置一个自定义序列来分配主键,但是我可以找到的用于创建自定义序列的唯一文档是非常基本的(如何增加两个而不是一个,等等).如何根据上述日期创建自定义序列?

Pat*_*ick 12

您可以使用以下EXTRACT()函数将手动创建的序列设置为特定值:

setval('my_sequence',
  (EXTRACT(YEAR FROM now())::integer * 1000000) +
  (EXTRACT(MONTH FROM now())::integer * 10000)
);
Run Code Online (Sandbox Code Playgroud)

输入的下一个订单将采用序列中的下一个值,即YYYYMM0001等.

诀窍是何时更新序列值.您可以在PG内部以硬盘方式执行此操作并BEFORE INSERT在订单表上写入一个触发器,以检查这是否是新月中的第一条记录:

CREATE FUNCTION before_insert_order() RETURNS trigger AS $$
DECLARE
  base_val  integer;
BEGIN
  -- base_val is the minimal value of the sequence for the current month: YYYYMM0000
  base_val := (EXTRACT(YEAR FROM now())::integer * 1000000) +
              (EXTRACT(MONTH FROM now())::integer * 10000);

  -- So if the sequence is less, then update it
  IF (currval('my_sequence') < base_val)
    setval('my_sequence', base_val);
  END IF;

  -- Now assign the order id and continue with the insert
  NEW.id := nextval('my_sequence');
  RETURN NEW;
END; $$ LANGUAGE plpgsql;

CREATE TRIGGER tr_bi_order
  BEFORE INSERT ON order_table
  FOR EACH ROW EXECUTE PROCEDURE before_insert_order();
Run Code Online (Sandbox Code Playgroud)

为什么这很难?因为您检查每个插入序列的值.如果您每天只有少量插入且系统不是很忙,这是一种可行的方法.

如果你不能省去所有这些CPU周期,你可以安排一个cron作业在每个月的第一天00:00:01运行,以执行PG功能psql来更新序列,然后只使用序列作为新的默认值订单记录(因此不需要触发器).

  • @Gene:`now()`给出了事务的开始时间.由于函数在逻辑上是一个事务,因此多个调用总是给出相同的时间. (4认同)