2014-06-10 66 views
5

我正在創建一個Rails應用程序,該應用程序將替換我的僱主的一個(非常舊的)舊版訂單管理應用程序。新系統的一個規格是訂單編號系統保持不變。眼下,我們的訂單號碼格式像這樣:如何根據行創建的日期在PostgreSQL中創建自定義序列?

  • 前四位是本年度
  • 接下來的兩個數字是當月
  • 下一個(也是最後一次)四位是一個計數器每次在該月份下單時增加1。

例如,2014年6月發佈的第一筆訂單的訂單號爲2014060001.下一個訂單的訂單號爲2014060002,依此類推。

此訂單號必須是訂單表中的主要ID。看起來我需要爲PostgreSQL設置一個自定義序列來分配主鍵,但是我可以找到創建自定義序列的唯一文檔是非常基本的(如何用兩個而不是一個等來增加)。如何根據上述日期創建自定義序列?

+2

對於這種情況,您不能使用序列,因爲事務失敗,序列可能會在數字中留下間隙:序列不會回滾。 –

回答

10

您可以使用EXTRACT()功能的手動創建序列設置爲一個特定值:

setval('my_sequence', 
    (EXTRACT(YEAR FROM now())::integer * 1000000) + 
    (EXTRACT(MONTH FROM now())::integer * 10000) 
); 

進入將採取下一個值的序列,即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(); 

爲什麼這是艱辛的道路?因爲您檢查每個插入的序列值。如果您每天只需插入少量插件,並且您的系統不是很忙,那麼這是一種可行的方法。

如果您不能備份所有這些CPU週期,您可以安排cron作業在每月的每個第一天的00:00:01運行,以通過psql執行PG功能來更新序列,然後只使用序列作爲新訂單記錄的默認值(因此不需要觸發器)。

+0

不錯並且非常有用,但是我只會打電話給'now()'一次,因爲計時器會在兩次調用之間嘀嗒一聲。或者是否有規範中的某些東西阻止了這一點? – Gene

+2

@Gene:'now()'給出事務開始時間。由於函數在邏輯上是一個事務,所以多次調用總是給出相同的時間。 – Patrick

+0

謝謝。這很有意義。 – Gene