Подтвердить что ты не робот

В функции триггера, как получить, какие поля обновляются

Возможно ли это? Мне интересно узнать, какие столбцы были указаны в запросе UPDATE, независимо от того, что новое значение, которое отправляется, может быть или не быть тем, что уже хранится в базе данных.

Причина, по которой я хочу сделать это, состоит в том, что у нас есть таблица, которая может получать обновления из нескольких источников. Раньше мы не записывали источник, из которого было произведено обновление. Теперь таблица хранит, какой источник выполнил последнее обновление. Мы можем изменить некоторые источники для отправки идентификатора, но это не вариант для всего. Поэтому я хотел бы узнать, когда запрос UPDATE не имеет идентификатора, поэтому я могу заменить его значением по умолчанию.

4b9b3361

Ответ 1

Как @A.H. написал в своем комментарии, если "источник" не "отправляет идентификатор", столбец не изменится. Тогда вы не можете определить, был ли текущий UPDATE выполнен тем же источником, что и последний, или источником, который вообще не менял столбца. Другими словами: это не работает должным образом.

Если "источник" идентифицируется любым из этими функциями информации сеанса, вы можете с этим справиться. Как:

NEW.column = session_user;

Безоговорочно для каждого обновления.


Общее решение

Я нашел способ решения исходной проблемы. Столбец будет установлен в значение по умолчанию в любом, где столбец не обновлен (не в SET списке UPDATE).

Ключевым элементом является триггеры для столбцов, представленные в PostgreSQL 9.0:

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

Это единственный простой способ, который я нашел, чтобы отличить, был ли обновлен столбец с новым значением, идентичным старому значению или вообще не обновленным.
Можно проанализировать текст, возвращенный current_query().

Я предполагаю, что столбец col определен NOT NULL.

  • Шаг 1: установите col на NULL, если он не изменился:

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step1()
  RETURNS trigger AS
$BODY$
BEGIN

IF OLD.col = NEW.col THEN
    NEW.col := NULL;        -- "impossible" value
END IF;

RETURN NEW;

END;
$BODY$  LANGUAGE plpgsql;
  • Шаг 2: возврат к старому значению. Триггер будет запущен, только если значение было фактически обновлено:

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step2()
  RETURNS trigger AS
$BODY$
BEGIN

IF NEW.col IS NULL THEN
    NEW.col := OLD.col;
END IF;

RETURN NEW;

END;
$BODY$  LANGUAGE plpgsql;
  • Шаг 3. Теперь мы можем определить недостающее обновление и установить значение по умолчанию:

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step3()
  RETURNS trigger AS
$BODY$
BEGIN

IF NEW.col IS NULL THEN
    NEW.col := 'default value';
END IF;

RETURN NEW;

END;
$BODY$  LANGUAGE plpgsql;

Реализовать триггеры, step2 - триггер за столбец:

CREATE TRIGGER upbef_step1
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step1();

CREATE TRIGGER upbef_step2
  BEFORE UPDATE OF col ON tbl               -- key element
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step2();

CREATE TRIGGER upbef_step3
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step3();

Имена триггеров актуальны, потому что они будут запущены в алфавитном порядке!

Процедура может быть упрощена, если у нас есть что-то вроде "триггеров без столбца" или любой другой способ легко проверить целевой список UPDATE в триггере.

Если ваша колонка может быть NULL, используйте любой другой "невозможный" темп. значение и проверьте для NULL дополнительно:

IF OLD.col IS NOT DISTINCT FROM NEW.col THEN
    NEW.col := '#impossible_value#';
END IF;

Ответ 2

В plpgsql вы можете сделать что-то подобное в своей триггерной функции:

IF NEW.column IS NULL THEN
  NEW.column = 'default value';
END IF;

Ответ 3

Я получил другое решение подобной проблемы почти естественно, потому что моя таблица содержала столбец с семантикой "последней метки времени обновления" (позволяет называть его UPDT).

Итак, я решил включить новые значения источника и UPDT в любое обновление только сразу (или ни одно из них). Поскольку UPDT предназначен для изменения при каждом обновлении, с такой политикой можно использовать условие new.UPDT = old.UPDT, чтобы вывести, что ни один источник не был указан с текущим обновлением и не заменил значение по умолчанию.

Если в его таблице уже есть столбец "последнего обновления времени", это решение будет проще, чем создание трех триггеров. Не уверен, что лучше создать UPDT, если он уже не нужен. Если обновления настолько часты, что существует риск сходства с меткой времени, вместо метки времени можно использовать секвенсор.