如何查询名称由 PL/pgSQL 函数结果构建的表?

问题描述 投票:0回答:1

我有下面的函数,它接受日期时间并返回一个整数。

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 postgresql plpgsql partitioning
1个回答
0
投票

功能更好(还是不要用!)

函数的更简单的 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
说明书:

date_part
函数以传统的 Ingres 为模型 相当于 SQL 标准函数
extract
:
[...]
为了 由于历史原因,
date_part
函数返回类型的值 双精度。这可能会导致某些方面的精度损失 用途。 建议使用
extract

我的粗体强调。

© www.soinside.com 2019 - 2024. All rights reserved.