我有下面的函数,它接受日期时间并返回一个整数。
CREATE OR REPLACE FUNCTION public.get_partition_index(datetime timestamp with time zone)
RETURNS integer
LANGUAGE plpgsql
IMMUTABLE
AS $function$
DECLARE
partitions integer := 52;
datetime_utc timestamp := datetime AT TIME ZONE 'UTC';
week_of_year integer := DATE_PART('week', datetime_utc);
partition_index integer := week_of_year % partitions;
BEGIN
RETURN partition_index;
END;
$function$
数据库中有 52 个表,名称为
metric_event_1
、metric_event_2
、...、metric_event_10
、... 和 metric_event_51
。
现在我想根据
get_partition_index
函数的结果查询表。
select * from `metric_event_+billing_event_partition_index_new(now())` limit 10;
但是上面的查询返回错误:
ERROR: syntax error at or near "`" LINE 1: select * from `billing_events_+billing_event_partition_index...
函数的更简单的 SQL 版本可以像这样工作:
CREATE OR REPLACE FUNCTION public.get_partition_index(datetime timestamptz)
RETURNS numeric
LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT EXTRACT(week FROM $1 AT TIME ZONE 'UTC') % 52;
$func$;
修复了语法并改进了技术细节。整个想法在多个层面上仍然是无稽之谈。
对于初学者来说,
names metric_event_1
... metric_event_51
,这是 51 张桌子,而不是您所说的 52 张桌子。实际上有 53 ISO 周。 EXTRACT
相应地返回从 1 到 53 的周数。你的% 52
会弄得一团糟,包括一周“0”......
您对反引号的尝试引发了语法错误,因为反引号不用于 Postgres(或根本标准 SQL)中的引用。参见:
但这种尝试在更深层次上是无稽之谈。您无法在纯 SQL 中参数化/插入标识符(包括表名称)。您可以在客户端中连接查询字符串,或使用动态 SQL:
CREATE OR REPLACE FUNCTION public.get_rows_from_metric_event(_tstz timestamptz, _limit int = 10)
RETURNS SETOF metric_event -- actual table name!
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
BEGIN
-- RAISE NOTICE '%', -- to debug
RETURN QUERY EXECUTE
format('SELECT * FROM metric_event_%s LIMIT %s', EXTRACT(week FROM _tstz AT TIME ZONE 'UTC') % 52, _limit);
END
$func$;
致电:
SELECT * FROM public.get_rows_from_metric_event(now());
还是废话。
如果
metric_event
是正确的 分区表 - 并且您默认启用了 enable_partition_pruning
- 只需使用与分区边界完全匹配的过滤器查询父表即可:
SELECT *
FROM metric_event
WHERE weeknr = EXTRACT(week FROM timestamptz '2024-04-07 00:00+2' AT TIME ZONE 'UTC')::int -- cast!
LIMIT 10;
假设这个基本表定义:
CREATE TABLE metric_event (weeknr int, data text) PARTITION BY LIST (weeknr);
CREATE TABLE metric_event_1 PARTITION OF metric_event FOR VALUES IN (1);
...
CREATE TABLE metric_event_53 PARTITION OF metric_event FOR VALUES IN (53);
分区修剪可以完成这项工作。您会得到一个查询计划,例如:
Limit (cost=0.00..25.88 rows=6 width=36)
-> Seq Scan on metric_event_14 metric_event (cost=0.00..25.88 rows=6 width=36)
Filter: (weeknr = 14)
分区边界必须完全匹配 - 包括数据类型! 请注意,
date_part()
返回 double precision
,而 EXTRACT
返回数字!在我的示例中,weeknr
是 integer
,所以转换为 int!参见:
EXTRACT
/ date_part()
EXTRACT
和 date_part()
基本上是等价的。 (EXTRACT
为大写,因为它实际上是一个语法元素而不是普通函数。)如果有疑问,请使用EXTRACT
。 说明书:
函数以传统的 Ingres 为模型 相当于 SQL 标准函数date_part
:extract
[...]
为了 由于历史原因,函数返回类型的值 双精度。这可能会导致某些方面的精度损失 用途。 建议使用date_part
。extract
我的粗体强调。