快速生成日期维度数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wzy0623/article/details/89051688

        日期维度在数据仓库中是一个特殊角色。日期维度包含时间概念,而时间是最重要的,因为数据仓库的主要功能之一就是存储和追溯历史数据,所以每个数据仓库里的数据都有一个时间特征。装载日期数据有三个常用方法:预装载、每日装载一天、从源数据装载日期。在三种方法中,预装载最为常见也最容易实现。在数据仓库生命周期中,只需要预装载日期维度一次。

        假设建立有如下日期维度表:

create table date_dim (    
    date_sk int,            -- 代理键  
    date date,              -- 日期
    month smallint,         -- 月份
    month_name varchar(9),  -- 月份名称
    quarter smallint,       -- 季度
    year smallint           -- 年份
);

        采用预装载方法一次性生成21年的日期维度数据,从2000年1月1日到2020年12月31日。在数据库中生成日期维度数据很简单,因为数据库一般都提供了丰富的日期时间函数,而且可以在存储过程中循环插入数据。下面对比HAWQ中两个生成日期数据函数的性能。

方法一:平凡低效

create or replace function fn_populate_date (start_dt date, end_dt date)    
returns void as $$    
declare    
    v_date date:= start_dt;   
    v_datediff int:= end_dt - start_dt;  
begin    
    for i in 0 .. v_datediff loop      
        insert into date_dim    
        values(i, 
               v_date, 
               extract(month from v_date), 
               to_char(v_date,'mon'), 
               extract(quarter from v_date), 
               extract(year from v_date));   
        v_date := v_date + 1;  
    end loop;  
    analyze date_dim;  
end; $$    
language plpgsql;

        关于这个函数没什么好说的,就是一个大循环,每次插入一条数据。以起始日期和终止日期参数的相差天数作为循环次数。在我的环境中执行这个函数需要将近9分钟,原因主要在于insert语句被执行了7671次。

postgres=# select fn_populate_date(date '2000-01-01', date '2020-12-31'); 
 fn_populate_date 
------------------
 
(1 row)

Time: 533999.903 ms

方法二:高效迭代

create or replace function fn_populate_date (start_dt date, end_dt date)  
returns void as  
$$  
declare  
    i int:=1;
    v_date date:= start_dt;
    v_datediff int:= end_dt - start_dt;
begin  
    truncate table date_dim;
    insert into date_dim(date_sk, date, month, month_name, quarter, year)
    values(i, 
           v_date, 
           extract(month from v_date), 
           to_char(v_date,'mon'), 
           extract(quarter from v_date), 
           extract(year from v_date));

    while i <= v_datediff
    loop    
        insert into date_dim(date_sk, date, month, month_name, quarter, year)  
        select date_sk + i, date + i, 
               extract(month from date+i),
               to_char(date+i,'mon'),
               extract(quarter from date+i),
               extract(year from date+i)
          from date_dim where date +i <= end_dt;

        i := i*2;
    end loop;
    analyze date_dim;
end;  
$$  
language plpgsql;

        这次执行只用了不到5秒钟。

postgres=# select fn_populate_date(date '2000-01-01', date '2020-12-31');
 fn_populate_date 
------------------
 
(1 row)

Time: 4987.249 ms

        在这个函数中,变量 i 保存插入date_dim表的行数。循环开始前先插入 1 条数据,然后当 date +i <= end_dt 成立时执行循环。在每次迭代中,该函数把日期维度表当前所有行的值加上 i 后再插入日期维度表中。这样每次循环插入的行数以2的幂次方递增,insert语句只被执行了14次,其中还包括作为种子数据的第一次插入。因此这个函数的执行速度很快。

        这种思想具有一定的通用性,例如在MySQL中生成数字辅助表数据时,就可以用下面的过程快速生成。

delimiter //
create procedure pfastcreatenums(cnt int)
begin
    declare s int default 1;
    truncate table nums;
    insert into nums select s;
    while s<=cnt do
        insert into nums select id+s from nums where id+s <=cnt;
        set s=s*2;
    end while;
    commit;
end;
//

猜你喜欢

转载自blog.csdn.net/wzy0623/article/details/89051688