Как запросить таблицу, имя которой построено на основе результата функции PL/pgSQL?

У меня есть функция ниже, которая принимает дату и время и возвращает целое число.

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...

Удалите обратную кавычку ` из вашего кода. Кроме того, это все равно не сработает, вы не сможете создать имя таблицы в FROM вот так. Прежде чем запрос может быть выполнен, имя таблицы должно быть ясным. Измените свою логику.

Frank Heikens 07.04.2024 22:00

Кстати, а зачем тебе это нужно? Обычно вы помещаете номер недели в условие WHERE и позволяете планировщику запросов выбрать правильный раздел.

Frank Heikens 07.04.2024 22:02

@FrankHeikens — это действительно старый код, таблица разделена на 52 части и использует триггеры для вставки повторяющихся записей в секционированные таблицы на основе приведенной выше функции. Теперь у меня другой запрос на получение последних 24 часов.

user51 08.04.2024 05:02
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
3
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Улучшенная функция (пока не пользуюсь!)

Более простая 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 возвращает numeric! В моем примере weeknr — это integer, поэтому приведите к integer! Видеть:

рабочий пример

О EXTRACT / date_part()

EXTRACT и date_part() в основном эквивалентны. (EXTRACT в верхнем регистре, потому что на самом деле это элемент синтаксиса, а не простая функция.) Если сомневаетесь, используйте EXTRACT. Инструкция:

Функция date_part создана по образцу традиционной функции Ingres. эквивалент стандартной функции SQL extract:
[...]
Для по историческим причинам функция date_part возвращает значения типа двойная точность. Это может привести к потере точности в некоторых случаях. использует. Вместо этого рекомендуется использовать extract.

Смелый акцент мой.

Проблема в том, что weeknr отсутствует в секционированных таблицах, и таблица секционируется не с использованием функции секционирования psql, а с использованием триггеров. Теперь мне нужно получить данные из дочерней таблицы за последние 24 часа.

user51 08.04.2024 05:06

@user51 Ваш триггер также знает, почему какое-то значение должно идти в раздел X, использует те же знания для секционированной таблицы, а также сохраняет номер недели (или дату). Добавление дополнительных обходных путей обычно не заканчивается хорошо

Frank Heikens 09.04.2024 05:49

Другие вопросы по теме