Использование TG_TABLE_NAME в триггерной функции postgresql в запросе SELECT

У меня есть триггерная функция 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 FUNCTION заявление, а не только тело. И покажите одно CREATE TRIGGER утверждение в дополнение к этому. А какой тип данных у prev_record.close?

Erwin Brandstetter 07.06.2024 00:07

Я отредактировал вопрос, включив их.

XotEmBotZ 07.06.2024 18:37

Вы пропустили утверждение CREATE TRIGGER. Я предполагаю BEFORE триггеры.

Erwin Brandstetter 07.06.2024 19:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Как правило, идентификаторы (включая имена таблиц) не могут быть параметризованы в простом 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 не работает, ошибка и код, приведенный выше. Проблема параллелизма также решается на стороне вставки.

XotEmBotZ 07.06.2024 18:38

Quote и base — это генерируемые столбцы. Ожидается ли, что они вызовут проблемы?

XotEmBotZ 08.06.2024 13:28

Кроме того, большинство целевых таблиц для этой функции имеют другую функцию, которая изменяет НОВУЮ (обе ДО), будет ли это также проблемой?

XotEmBotZ 08.06.2024 13:34
Руководство: «Сохраненные сгенерированные столбцы вычисляются после BEFORE триггеров и перед AFTER триггерами». Это триггерная функция для триггера BEFORE (который вы не раскрыли), так что да, это не сработает. Вам нужно сделать больше. Но это никогда не было частью вопроса.
Erwin Brandstetter 08.06.2024 15:13

Несколько триггеров по одному и тому же событию срабатывают в алфавитном порядке (по имени триггера, а не по имени функции!)

Erwin Brandstetter 08.06.2024 15:14

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