У меня есть функция ниже, которая принимает дату и время и возвращает целое число.
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...
Кстати, а зачем тебе это нужно? Обычно вы помещаете номер недели в условие WHERE и позволяете планировщику запросов выбрать правильный раздел.
@FrankHeikens — это действительно старый код, таблица разделена на 52 части и использует триггеры для вставки повторяющихся записей в секционированные таблицы на основе приведенной выше функции. Теперь у меня другой запрос на получение последних 24 часов.


Более простая 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. эквивалент стандартной функции SQLextract:
[...]
Для по историческим причинам функцияdate_partвозвращает значения типа двойная точность. Это может привести к потере точности в некоторых случаях. использует. Вместо этого рекомендуется использоватьextract.
Смелый акцент мой.
Проблема в том, что weeknr отсутствует в секционированных таблицах, и таблица секционируется не с использованием функции секционирования psql, а с использованием триггеров. Теперь мне нужно получить данные из дочерней таблицы за последние 24 часа.
@user51 Ваш триггер также знает, почему какое-то значение должно идти в раздел X, использует те же знания для секционированной таблицы, а также сохраняет номер недели (или дату). Добавление дополнительных обходных путей обычно не заканчивается хорошо
Удалите обратную кавычку ` из вашего кода. Кроме того, это все равно не сработает, вы не сможете создать имя таблицы в FROM вот так. Прежде чем запрос может быть выполнен, имя таблицы должно быть ясным. Измените свою логику.