0

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

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

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

Это исходная таблица (для простоты предположим, что таблица назначения имеет ту же структуру):

CREATE TABLE SRC_DEPARTMENTS 
(
    ID_DEPARTMENT INT NOT NULL PRIMARY KEY, 
    NAME VARCHAR(80) NOT NULL, 
    ID_PARENT_DEPARTMENT INT, 
    HIERARCHY VARCHAR(50) NOT NULL, 
    ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, 

    FOREIGN KEY (ID_PARENT_DEPARTMENT) REFERENCES SRC_DEPARTMENTS (ID_DEPARTMENT) ON DELETE CASCADE 
); 

Теперь предположим, что у меня есть эти строки в таблице назначения:

ID_DEPARTMENT | ID_PARENT_DEPARTMENT | HIERARCHY 
--------------+----------------------+---------- 
      1 |      | 1 
      2 |     1 | 1.2 
      3 |     2 | 1.2.3 
      4 |     3 | 1.2.3.4 
      5 |      | 5 
      6 |     5 | 5.6 

, и я хочу, чтобы изменить родительский отдел ид от 6 до точки к идентификатору 4. После изменения строки должна быть:

ID_DEPARTMENT | ID_PARENT_DEPARTMENT | HIERARCHY 
--------------+----------------------+---------- 
      1 |      | 1 
      2 |     1 | 1.2 
      3 |     2 | 1.2.3 
      4 |     3 | 1.2.3.4 
      6 |     4 | 1.2.3.4.6 
      5 |      | 5 

Итак, как вы можете видеть, обновление было затронуто только одной строкой. Теперь предположим, что я хочу изменить родительский идентификатор id 1 (это NULL), чтобы указать на id 6 (в исходном наборе строк). Таким образом, после изменения вы будете иметь:

ID_DEPARTMENT | ID_PARENT_DEPARTMENT | HIERARCHY 
--------------+----------------------+---------- 
      5 |      | 5 
      6 |     5 | 5.6 
      1 |     6 | 5.6.1 
      2 |     1 | 5.6.1.2 
      3 |     2 | 5.6.1.2.3 
      4 |     3 | 5.6.1.2.3.4 

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

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

CREATE OR REPLACE FUNCTION insert_update_department() RETURNS trigger AS $$ 
DECLARE 
    _id_parent_department INT; 
    _id_parent_department_changed BOOLEAN := FALSE; 
    _hierarchy VARCHAR(50); 
    _current_hierarchy VARCHAR(50); 
BEGIN 
    IF TG_OP = 'UPDATE' AND (
     NEW.NAME IS NOT DISTINCT FROM OLD.NAME AND 
     NEW.ID_PARENT_DEPARTMENT IS NOT DISTINCT FROM OLD.ID_PARENT_DEPARTMENT AND 
     NEW.ACTIVE IS NOT DISTINCT FROM OLD.ACTIVE) THEN 
     RETURN NULL; 
    END IF; 

    IF TG_OP = 'INSERT' OR NEW.ID_PARENT_DEPARTMENT IS DISTINCT FROM OLD.ID_PARENT_DEPARTMENT THEN 
     IF NEW.ID_PARENT_DEPARTMENT IS NULL OR NEW.ID_PARENT_DEPARTMENT = NEW.ID_PARENT_DEPARTMENT THEN 
      _id_parent_department := NULL; 
     ELSE 
      _id_parent_department := NEW.ID_PARENT_DEPARTMENT; 
     END IF; 

     IF _id_parent_department IS NULL THEN 
      _hierarchy := ''; 
     ELSE 
      SELECT HIERARCHY || '.' 
      INTO _hierarchy 
      FROM DST_DEPARTMENTS 
      WHERE ID_DEPARTMENT = _id_parent_department; 
     END IF; 
     _hierarchy := _hierarchy || cast(NEW.ID_DEPARTMENT AS TEXT); 

     IF TG_OP = 'UPDATE' THEN 
      SELECT HIERARCHY || '.' 
      INTO _current_hierarchy 
      FROM DST_DEPARTMENTS 
      WHERE ID_DEPARTMENT = NEW.ID_DEPARTMENT; 

      UPDATE DST_DEPARTMENTS SET 
       HIERARCHY = _hierarchy || '.' || substr(HIERARCHY, length(_current_hierarchy) + 1) 
      WHERE HIERARCHY LIKE _current_hierarchy || '%'; 
     END IF; 

     _id_parent_department_changed := TRUE; 
    END IF; 

    IF TG_OP = 'INSERT' THEN 
     INSERT INTO DST_DEPARTMENTS VALUES (
      NEW.ID_DEPARTMENT, 
      _name, 
      _id_parent_department, 
      _hierarchy, 
      NEW.ACTIVE 
     ); 
    ELSE 
     UPDATE DST_DEPARTMENTS SET 
      NAME = _name, 
      ID_PARENT_DEPARTMENT = CASE WHEN _id_parent_department_changed THEN _id_parent_department ELSE ID_PARENT_DEPARTMENT END, 
      HIERARCHY = CASE WHEN _id_parent_department_changed THEN _hierarchy ELSE HIERARCHY END, 
      ACTIVE = NEW.ACTIVE 
     WHERE ID_DEPARTMENT = NEW.ID_DEPARTMENT; 
    END IF; 

    RETURN NULL; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER z_insert_update_department 
    AFTER INSERT OR UPDATE ON SRC_DEPARTMENTS 
    FOR EACH ROW 
    EXECUTE PROCEDURE insert_update_department(); 

Возможно изменение эти строки из этого:

SELECT HIERARCHY || '.' 
INTO _current_hierarchy 
FROM DST_DEPARTMENTS 
WHERE ID_DEPARTMENT = NEW.ID_DEPARTMENT; 

к этому:

SELECT HIERARCHY || '.' 
INTO _current_hierarchy 
FROM DST_DEPARTMENTS 
WHERE ID_DEPARTMENT = NEW.ID_DEPARTMENT 
FOR UPDATE; 

будет решить эту проблему для текущей строки, но не для других строк которые необходимо обновить.

Я очень рад, если кто-то скажет мне, что правильно делать, чтобы исправить срабатывание, чтобы работать правильно одновременно.

Заранее спасибо.

Marcos

+0

'два стола, живущих в разных базах данных.' Различные базы данных являются частью ** одной ** транзакции? Вам, по крайней мере, потребуется два фазовых фиксации (на самом деле это две синхронизированные транзакции) – joop

+0

@joop Используя _postgres_fdw_ [link] (http://www.postgresql.org/docs/9.3/static/postgres-fdw.html), вы можете сделать две таблицы, живущие в разных базах данных, одной и той же транзакцией. Это то, что я использую. Триггер уже запущен в транзакции. – Marcos

+0

@joop Более подробно см. Раздел _F.31.3. Управление транзакциями в (http://www.postgresql.org/docs/9.3/static/postgres-fdw.html). – Marcos

ответ

0

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

+0

Триггер уже запущен под транзакцией, запущенной кодом приложения, но транзакции не будут изолировать вас от проблем, возникающих из-за параллельных проблем, таких как _dirty read_, _nonrepeableable read_ и т. Д. (Http://www.postgresql.org/ документы/9.3/статический/транзакций iso.html). Вы должны правильно закодировать свои запросы и обновления, чтобы избежать этих проблем. Вот что я ищу здесь. Если кто-то может указать любой из этих недостатков в триггете выше, я был бы очень благодарен. – Marcos