Управляющее поле

Обновлено: 14 февраля, 2025

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

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

Задачи управляющего поля

Обычное у меня название этого поля — _trigger_off. Изначально оно предназначалось для того, что бе когда производится INSERT или UPDATE, то если в запросе оно передается как TRUE, то в триггере происходил сразу возврат из процедуры с обнулением этого поля, то есть по сути триггерная функция вызывалась, но никаких действий не производила. В данный момент понятие этого поля трансформировалось в более широкое понятие, но название осталось прежним, так что связывать название этого поля с его значение в прямом смысле не имеет значения, вы можете его называть как вам удобно, я называю так, по привычке.

Тип поля — BOOLEAN. На самом деле, при условии что поле может быть NULL, то вариантов значений поля становится не 2 (TRUE, FALSE), а 4 (TRUE, FALSE, NULL, NOT NULL), это важно и вот почему:

  • Данное поле позволяет определить стоит ли исполнять основной код триггерной функции или нет (TRUE или FALSE);
  • Так же оно позволяет определить откуда был произведен запрос, из приложения или из соседнего триггера (NULL или NOT NULL);

В сухом остатке задачи управляющего поля можно определить таким образом: понять откуда пришел запрос и что с ним делать. И что бы на эти вопросы был однозначный ответ, требуется придерживаться определенных правил.

Правила управляющего поля

1. Поле в таблице ВСЕГДА сохранятся NULL

То есть, какое из значений поля не передавалось бы, в результате оно обязательно приводится к NULL. При чем, не зависимо от того, все ли триггера есть или только на определенные запросы, требуется ставить триггер-заглушку, которая сбросит значение поля в NULL. Для удобства я делаю одну общую хранимую процедуру:

CREATE FUNCTION "public"."flush_trigger_off" (
)
RETURNS TRIGGER AS
$body$
BEGIN
    NEW._trigger_off := NULL;
    RETURN NEW;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE;

А после создания таблицы в которой присутсвует управляющее поле, сразу создаю для нее триггер:

CREATE TRIGGER flush_control_field
    BEFORE INSERT OR UPDATE 
    ON [table_name] FOR EACH ROW 
    EXECUTE PROCEDURE "public"."flush_trigger_off"();

И тогда не надо будет переживать по поводу того, что триггеры еще не написаны, но данные уже льются, и мало ли.

Соответсвенно при соблюдении данного правила, значение данного поля по-умочанию будет NULL, как в новой (NEW) так и в старой (OLD) строке.

Так же это правило, как следствие экономит место, так так поле всегда NULL, то места физически оно не занимает, только виртуально в разметке строк.

2. Данное поле НЕ должно использоваться внешним приложением

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

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

По большому счету значение поля NULL говорит о том, что триггер должен сам озаботится о целостности данных и решить что и как делать.

3. Данное поле должно ВСЕГДА использоваться во внутренних запросах

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

Здесь, конечно стоит разделять запросы обеспечения целостности данных и связей от обычных запросов, которые не влияют на целостность. Поэтому устанавливать принудительно это поле отличное от нуля для абсолютно всех подзапросов на уровне базы данных — не имеет смысла.

В итоге

Использование подобного поля позволяет контролировать точки входа запросов и принимать соответсвующие решения.

Опубликовано: 14 февраля, 2025

ЕЩЕ СТАТЬИ ПО ДАННОЙ ТЕМЕ

Преимущества и недостатки PostgreSQL в мире бизнес-данных

Пользователи сообщают о разнице в производительности при миграции. PostgreSQL сталкивается с проблемами при работе с некоторыми запросами, особенно по сравнению с SQL Server. Усовершенствования возможны через многопоточность и улучшенное кэширование.

Читать далее »

Поддержка Postgre SQL

Поддержка — это когда у вас возникает техническая
проблема с существующей системой,
и вам необходимо некоторое руководство