首先,我们创建一个函数,该函数返回一个表,该表有一个字段和记录,从 time_start 值开始并增加一分钟。
CREATE OR REPLACE FUNCTION get_dates(time_start timestamp without time zone)
RETURNS TABLE(list_dates timestamp without time zone)
LANGUAGE plpgsql
AS $function$
declare
time_end timestamp;
begin
time_end = time_start + interval '1 day';
return query
SELECT t1.dates
FROM generate_series(time_start, time_end, interval '1 min') t1(dates);
END;
$function$
;
考虑一下,这个函数返回的时间戳列表有一个小时和分钟,但秒和毫秒总是等于零。我们必须使用timestamp 字段将这个函数结果与我们的customer 表连接起来。但是我们customer 表的时间戳字段是完整的DateTime 格式,秒和毫秒不为空。因此,可以理解我们的连接条件将不正确。我们需要这样,在customer 表中,时间戳字段的所有数据总是有一个空的秒和空的毫秒来正确连接。为此,我创建了一个不可变函数,当您向该函数发送完全格式化的 DateTime 2021-02-03 18:24:51.203 时,该函数将返回空秒和空毫秒的时间戳,格式为:2021-02-03 18:24:00.000
CREATE OR REPLACE FUNCTION clear_seconds_from_datetime(dt timestamp without time zone)
RETURNS timestamp without time zone
LANGUAGE plpgsql
IMMUTABLE
AS $function$
DECLARE
str_date text;
str_hour text;
str_min text;
v_ret timestamp;
begin
str_date = (dt::date)::text;
str_hour = (extract(hour from dt))::text;
str_min = (extract(min from dt))::text;
str_date = str_date || ' ' || str_hour || ':' || str_min || ':' || '00';
v_ret = str_date::timestamp;
return v_ret;
END;
$function$
;
为什么我们的函数是不可变的?为了获得高性能,我想在 PostgreSQL 上使用基于函数的索引。但是,PostgreSQL 只需要在索引上使用不可变函数。我们在加入过程的条件下使用这个函数。现在我们来看看创建基于函数的索引的过程:
CREATE INDEX customer_date_function_idx ON customer USING btree ((clear_seconds_from_datetime(datestamp)));
让我们编写我们需要的主要查询:
select
t1.list_dates,
cc.userid,
count(cc.id) as count_as
from
get_dates('2021-02-03 00:01:00'::timestamp) t1 (list_dates)
left join
customer cc on clear_seconds_from_datetime(cc.datestamp) = t1.list_dates
group by t1.list_dates, cc.userid
我将 3000 万个样本数据插入到 customer 表中进行测试。但是,这个查询运行了 33 毫秒。查看生成的explain analyze 命令的查询计划:
HashAggregate (cost=309264.30..309432.30 rows=16800 width=20) (actual time=26.076..26.958 rows=2022 loops=1)
Group Key: t1.list_dates, lg.userid
-> Nested Loop Left Join (cost=0.68..253482.75 rows=7437540 width=16) (actual time=18.870..24.882 rows=2023 loops=1)
-> Function Scan on get_dates t1 (cost=0.25..10.25 rows=1000 width=8) (actual time=18.699..18.906 rows=1441 loops=1)
-> Index Scan using log_date_cast_idx on log lg (cost=0.43..179.09 rows=7438 width=16) (actual time=0.003..0.003 rows=1 loops=1441)
Index Cond: (clear_seconds_from_datetime(datestamp) = t1.list_dates)
Planning Time: 0.398 ms
JIT:
Functions: 12
Options: Inlining false, Optimization false, Expressions true, Deforming true
Timing: Generation 3.544 ms, Inlining 0.000 ms, Optimization 0.816 ms, Emission 16.709 ms, Total 21.069 ms
Execution Time: 31.429 ms
结果:
| list_dates |
group_count |
| 2021-02-03 09:41:00.000 |
1 |
| 2021-02-03 09:42:00.000 |
3 |
| 2021-02-03 09:43:00.000 |
1 |
| 2021-02-03 09:44:00.000 |
3 |
| 2021-02-03 09:45:00.000 |
1 |
| 2021-02-03 09:46:00.000 |
5 |
| 2021-02-03 09:47:00.000 |
2 |
| 2021-02-03 09:48:00.000 |
1 |
| 2021-02-03 09:49:00.000 |
1 |
| 2021-02-03 09:50:00.000 |
1 |
| 2021-02-03 09:51:00.000 |
4 |
| 2021-02-03 09:52:00.000 |
0 |
| 2021-02-03 09:53:00.000 |
0 |
| 2021-02-03 09:54:00.000 |
2 |
| 2021-02-03 09:55:00.000 |
1 |