У меня есть триггерная функция plpgsql
DECLARE
prev_record daily_data%ROWTYPE;
BEGIN
SELECT * INTO prev_record
FROM ONLY TG_TABLE_NAME
WHERE base = NEW.quote AND quote = 'USDT'
ORDER BY timestamp DESC
LIMIT 1;
IF EXISTS prev_record THEN
NEW.volume_usd := NEW.volume * prev_record.close;
END IF;
RETURN NEW;
END;
Основной целью этой функции является получение последнего значения из таблицы и использование его для обновления Volume_usd нового вставляемого значения.
Эта функция будет использоваться во многих таблицах, поэтому я использовал TG_TABLE_NAME в операторе выбора, но, похоже, она выдает ошибку.
ERROR: relation "tg_table_name" does not exist at character 41
2024-06-06 16:32:57.222 UTC [3253] QUERY: SELECT * FROM ONLY TG_TABLE_NAME
WHERE base = NEW.quote AND quote = 'USDT'
ORDER BY timestamp DESC
LIMIT 1
CONTEXT: PL/pgSQL function update_volume_usd() line 5 at SQL statement
Я рассмотрел функцию выполнения, но не думаю, что она будет здесь полезна, поскольку мне потребуется ее сохранить.
Редактировать: Я пробовал использовать EXECUTE, но это не работает.
CREATE OR REPLACE FUNCTION public.update_volume_usd()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
DECLARE
prev_record daily_data%ROWTYPE;
BEGIN
EXECUTE format('
SELECT * INTO prev_record
FROM ONLY %I
WHERE base = $1 AND quote = ''USDT''
ORDER BY timestamp DESC
LIMIT 1
', TG_TABLE_NAME) USING NEW.quote;
IF EXISTS prev_record THEN
NEW.volume_usd := NEW.volume * prev_record.close;
END IF;
RETURN NEW;
END;
$BODY$;
ОШИБКА:
ERROR: EXECUTE of SELECT ... INTO is not implemented
HINT: You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.
CONTEXT: PL/pgSQL function update_volume_usd() line 4 at EXECUTE
SQL state: 0A000
тип данных prev_record.close — ЧИСЛОВОЙ
Я отредактировал вопрос, включив их.
Вы пропустили утверждение CREATE TRIGGER. Я предполагаю BEFORE триггеры.





Как правило, идентификаторы (включая имена таблиц) не могут быть параметризованы в простом SQL. Для этого вам понадобится динамический SQL с EXECUTE.
Кроме того, IF EXISTS prev_record THEN не является допустимым синтаксисом. IF FOUND ... работал бы на своем месте.
Но логика этого триггера разваливается при одновременной загрузке записи. Несколько одновременных транзакций будут вычислять бессвязные значения из-за проблем с видимостью. Либо используйте уровень изоляции SERIALIZABLE, либо вообще не пробуйте этот волшебный трюк, а вместо этого сохраняйте простые значения и выполняйте вычисления по временным рядам в запросе/представлении/материализованном представлении.
Тем не менее, вот допустимое определение функции:
CREATE OR REPLACE FUNCTION public.update_volume_usd()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
DECLARE
_prev_close numeric; -- we only need this column
BEGIN
EXECUTE format(
$q$
SELECT close
FROM ONLY %I
WHERE base = $1 -- compare base to NEW.quote?
AND quote = 'USDT'
ORDER BY timestamp DESC -- must be defined NOT NULL !
LIMIT 1
$q$, TG_TABLE_NAME)
USING NEW.quote
INTO _prev_close;
IF _prev_close IS NOT NULL THEN
NEW.volume_usd := NEW.volume * _prev_close;
END IF;
RETURN NEW;
END
$func$;
Необходимо использовать в триггере BEFORE.
При использовании EXECUTE предложение INTO не может быть вложено в динамический запрос, его необходимо добавить к команде EXECUTE, как показано. Подробности в инструкции.
После перехода на динамический SQL мой первоначальный совет по использованию FOUND недействителен. Инструкция:
Обратите внимание, в частности, что
EXECUTEменяет выводGET DIAGNOSTICS, но не меняетFOUND.
В этом случае мы также можем проверить _prev_close IS NOT NULL — предполагая, что столбец определен NOT NULL.
Моя реализация EXECUTE не работает, ошибка и код, приведенный выше. Проблема параллелизма также решается на стороне вставки.
Quote и base — это генерируемые столбцы. Ожидается ли, что они вызовут проблемы?
Кроме того, большинство целевых таблиц для этой функции имеют другую функцию, которая изменяет НОВУЮ (обе ДО), будет ли это также проблемой?
BEFORE триггеров и перед AFTER триггерами». Это триггерная функция для триггера BEFORE (который вы не раскрыли), так что да, это не сработает. Вам нужно сделать больше. Но это никогда не было частью вопроса.
Несколько триггеров по одному и тому же событию срабатывают в алфавитном порядке (по имени триггера, а не по имени функции!)
Пожалуйста (всегда) публикуйте полное
CREATE FUNCTIONзаявление, а не только тело. И покажите одноCREATE TRIGGERутверждение в дополнение к этому. А какой тип данных уprev_record.close?