DOC.PROTOTYPES.RU

Главная > Базы данных > PostgreSQL > Рецепты >

Рецепты PostgreSQL

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

Введение

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

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

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

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

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

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

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

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

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

SQL код (1)
CREATE FUNCTION "public"."flush_trigger_off" (
)
RETURNS trigger AS
$body$
BEGIN
    NEW._trigger_off := NULL;
    RETURN NEW;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE;

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

SQL код (2)
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. Данное поле должно ВСЕГДА использоваться во внутренних запросах

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

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

В итоге

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

Сергей Томулевич aka Phoinix (21.04.2014 г.)
Copyright © 2011 Сергей Томулевич