with get_func as (
select sample_start_time,a.snap_id, schemaname AS schema_name, funcname AS func_name,round(calls) AS calls,
round(total_time) AS total_time, round(self_time) AS self_time
from pg_stat_user_functions_history a, pg_awr_snapshots_cust b 
where a.snap_id = b.snap_id
and a.snap_id between :begin_snap_id and :end_snap_id
order by schemaname,funcname,a.snap_id
),
get_lag_data as (
select sample_start_time,schema_name,func_name,snap_id,calls,
case WHEN (calls-lag(calls::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) = calls then null
else (calls-lag(calls::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) END AS delta_calls,
case WHEN (total_time-lag(total_time::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) = total_time then null
else (total_time-lag(total_time::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) END as Delta_total_time,
case WHEN (self_time-lag(self_time::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) = self_time then null
else (self_time-lag(self_time::numeric,1,0::numeric) OVER (partition by schema_name,func_name ORDER BY snap_id) ) END as Delta_self_time
from get_func
where  snap_id between :begin_snap_id-1 and :end_snap_id
)
,time_partitioned_data as (
select schema_name,func_name,round((sum(Delta_total_time)/sum(delta_calls))::numeric,2) AS AVG_TOTAL_TIME,
sum(delta_calls) AS calls,
round((sum(Delta_self_time)/sum(delta_calls))::numeric) AS AVG_SELF_TIME
from get_lag_data
where Delta_total_time is not null
and delta_calls is not null
group by schema_name,func_name
having sum(delta_calls)>0
)
select * from time_partitioned_data
order by AVG_TOTAL_TIME desc
limit 20;