2016-11-09 43 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

相关问题