2016-11-09 29 views
0

我希望以一種很好的方式跟蹤網站上某個項目的價格。一種選擇是隻在每次我更新價格正好在這個表中創建一個新的行時間像postgresql 9.5 - 隨着時間的推移跟蹤對一個值的更改

create table prices (
    name text, 
    price decimal, 
    updated timestamp 
) 

一張桌子和。 不過,我認爲這種做法是不是在我的處境很「好」,有以下原因:

  1. 我跟蹤的幾個1000個項目在任何給定的時間,
  2. 我更新價格每上漲5或等分鐘,
  3. 價格通常不會變化很多,有時根本不會改變。

由於這些原因,對於一個特定的項目,說:dove bodywash,我的價格表可以包含200列,所有看起來像

'dove soap' | 3.00 | <a new timestamp every 5 minutes> 

這似乎有點可笑。 在我看來,一個更好的解決方案是增加一個額外的price_history表格,該表格存儲了一個物品具有特定價格的時間範圍。例如,price_history可能有列

name | price | created_at | updated_at 

和期望的行爲是,每當我更新的dove soap價格,會有一個觸發器,它會自動檢查,如果價格發生了變化 - 如果它不,那麼只需更改prices_history中相應條目的updated_at,如果有,則爲新價格在prices_history中創建一個新行。作爲執行的例子,我想要:

1)在時間1我做insert into prices ('dove soap', 3.00)。在這一點上,price_history表將包含在時間2行

|'dove soap' | 3.00 | time1 | null | 

2)我做update prices set price = 3.00 where name = 'dove soap'。現在price_history表看起來像

'dove soap' | 3.00 | time1 | time2 

3)在時間3的價格仍然是3.00。 price_history應該看起來像

'dove soap' | 3.00 | time1| time3 

4)在時間4價格是3.50。 price_history現在應該是這樣的:

'dove soap' | 3.00 | time1 | time3 
'dove soap' | 3.50 | time4 | null 

我的問題是

  1. 我不知道這是否是去了解一個很好的方式,並且
  2. 我不是100%確定是什麼一個好的方法來實現這一點將是。

任何意見上述任何一點都非常感謝!

感謝:-)

編輯:我應該包括一兩件事,我看着是temporal_tables PostgreSQL的擴展,它採用了類似的價格/ price_history /套起。問題在於,它似乎會在價格每次更新時在price_history表中創建一個新行,如果它沒有更改則會創建一個新行,這會破壞目的。在我看來,沒有辦法修改這種默認行爲,但如果有人知道更好,請讓我知道!

+0

我有類似的情況。我的每條記錄都有開始日期和結束日期。無論何時添加記錄(帶有新的開始日期),都會觸發一個觸發器,以適當的日期更新先前記錄的結束日期。在你的情況下,我會在插入之前添加一個觸發器,如果​​價格相同,則不插入。如果沒有改變,不會麻煩改變updated_at。最後,我會將你所謂的price_history作爲你唯一的表格和一個提取當前記錄的視圖。 – mlinth

+0

感謝您的回覆!你有沒有機會鏈接到創建這種觸發器的例子?我對sql相當陌生,對事物的工作原理沒有很好的理解。 – ira

回答

2

這是一個可以工作的設計,使用一張桌子和一個視圖...我做了一些假設,即你並不真正關心跟蹤上次更新時間(但見下文),而且最新條目的結束時間是2999-12-31 23:59:59。 (你可以留空,但我不喜歡空值,並在那裏有一個日期意味着你可以在查詢之間做...)。

創建price_history_table:

create table price_history(

article_id integer, -- I like using article ids 
article_name text, -- I don't like using reserved words for columns 
price decimal not null, 
start_time timestamp not null, 
end_time timestamp not null default '2999-12-31 23:59:59') 

(如果你不希望使用的article_id,與ARTICLE_NAME更換整個的article_id下面,雖然你可能會考慮將你的項目說明在一個單獨的表,只有存儲id在「大」表中,佔用磁盤空間較少,一列寫少)。

上創建的article_id和結束時間的唯一約束:

alter table price_history add constraint article_id_end_time unique (article_id,end_time) 

...和article_id的主鍵和START_TIME

alter table price_history add constraint pk_price_history primary key (article_id,start_time); 

我認爲有這些限制,以防止這一點很重要你在表格中輸入垃圾,因爲重複的次數會破壞你的邏輯。

現在觸發功能。如果價格沒有改變,觸發器什麼都不做,否則它將最後一條記錄的end_time更新爲新的start_time。

CREATE FUNCTION update_enddate() 
    RETURNS trigger 
    LANGUAGE 'plpgsql' 
    COST 100.0 
    VOLATILE NOT LEAKPROOF 
AS $BODY$ 

BEGIN 



    if EXISTS (select * from price_history where article_id = NEW.article_id AND end_time ='2999-12-31 23:59:59'::timestamp AND price = NEW.price) THEN 
    -- the price hasn't changed, don't do anything 

    RETURN NULL; 

    ELSE --Set the end date to the new startdate 
      update price_history set end_time = NEW.start_time where article_id = new.article_id AND end_time ='2999-12-31 23:59:59'::timestamp; 
     RETURN NEW; 
    END IF; 



    END; 

$BODY$; 

而觸發本身。

CREATE TRIGGER trigger_update_enddate BEFORE INSERT on price_history FOR EACH ROW EXECUTE PROCEDURE update_enddate(); 

並且查看最近的記錄。

CREATE VIEW prices AS 
    SELECT article_id,article_name,price,start_time from price_history where end_time ='2999-12-31 23:59:59'::timestamp; 

如果你想找出一個價格是否改變,你需要小心一點與查詢「之間」,因爲他們包括一個給定的更新,你可以嘗試像

SELECT * from price_history where start_time <= mytime and end_time > mytime; 

注意的事情開始和結束點,如果您的時間恰好與start_time匹配,您可能會得到重複。

start_time等於上次更改價格的時間。您可以將更新時間存儲在不同的表中,只需加入start_time < = update_time和end_time> update_time即可獲得「完整歷史記錄」。

如果您不斷添加記錄,則無法確定索引的性能,因此如果您沒有索引,可能會獲得更好的性能。

+0

非常感謝你的充分充實的反應,這是非常有益的。不幸的是,我*需要記錄上次更新的時間。我在最後看到了你的評論......但是我是否也可以修改觸發器函數在if/else的第一種情況下對現有行進行更新? – ira

+0

你可以這樣做,但你需要仔細考慮事情的進展情況......例如你可能想要第三列last_updated,是的,你可以修改觸發器(SELECT NEW.update_time = now()) – mlinth

相關問題