postgresql 9.5 - 跟踪随时间变化的值

时间:2016-11-09 18:39:07

标签: sql postgresql postgresql-9.5

我希望以一种不错的方式跟踪网站上某个商品的价格。一种选择是只有一个像

这样的表
create table prices (
     name text,
     price decimal,
     updated timestamp
)

每次更新价格时,只需在此表格中创建一个新行。 但是,我认为这种做法不是很好,而且很好。在我的情况下,原因如下:

  1. 我正在跟踪任何给定时间的1000件物品,
  2. 我每5分钟更新一次价格,
  3. 价格通常不会经常变化,有时根本不会发生变化。
  4. 由于这些原因,对于特定项目,例如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的价格时,会有一个触发器会自动检查价格是否已经改变 - 如果它没有改变,那么只需更改price_history中相应条目的updated_at,如果有,则在price_history中为新价格创建一个新行。作为执行的一个例子,我想:

    1)在时间1我做insert into prices ('dove soap', 3.00)。此时,price_history表将包含一行

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

    2)在时间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%确定实现这一目标的好方法。
    3. 非常感谢上述任何一点的任何建议!

      谢谢: - )

      编辑:我应该包括我看到的一件事是temporal_tables postgresql扩展,它使用类似的价格/ price_history /设置。它的问题在于,每次价格更新时,它似乎会在price_history表中创建一个新行,如果它没有改变则会发生事件,这会使目的失效。在我看来,没有办法修改这种默认行为,但如果有人知道更好,请告诉我!

1 个答案:

答案 0 :(得分: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_id替换为article_name,尽管您可能会考虑将项目描述存储在单独的表中并仅在“大”表中存储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&lt; = update_time和end_time&gt; update_time为您提供“完整历史记录”。

如果您不断添加记录,则不确定索引的性能,因此如果您没有索引,可能会获得更好的性能。