У меня есть задача синхронизировать две таблицы, живущие в разных базах данных. Поэтому для каждой вставки, обновления и удаления, которые происходят в исходной таблице, эти изменения должны быть реплицированы в таблице адресатов. Таблица назначения будет клоном исходной таблицы. Чтобы реализовать это, я решил установить триггеры в исходной таблице.Последовательное обновление нескольких строк в таблице, выделение из параллельных помех
Но я глубоко обеспокоен параллельными аспектами этих обновлений, поскольку несколько пользователей используют таблицы одновременно, а иногда триггеры должны обновлять несколько строк в таблице адресатов. С точки зрения изменений, которые должны быть выполнены в триггерах, я очень убежден, что логика правильная, но не об уровнях изоляции, потому что я не эксперт в этом.
Итак, я покажу вам триггер, который отвечает за вставки и обновления в таблице адресатов и попросит вас узнать, есть ли какие-либо проблемы в отношении параллельного аспекта. Но перед этим, позвольте мне показать вам таблицу и некоторые случаи использования:
Это исходная таблица (для простоты предположим, что таблица назначения имеет ту же структуру):
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
'два стола, живущих в разных базах данных.' Различные базы данных являются частью ** одной ** транзакции? Вам, по крайней мере, потребуется два фазовых фиксации (на самом деле это две синхронизированные транзакции) – joop
@joop Используя _postgres_fdw_ [link] (http://www.postgresql.org/docs/9.3/static/postgres-fdw.html), вы можете сделать две таблицы, живущие в разных базах данных, одной и той же транзакцией. Это то, что я использую. Триггер уже запущен в транзакции. – Marcos
@joop Более подробно см. Раздел _F.31.3. Управление транзакциями в (http://www.postgresql.org/docs/9.3/static/postgres-fdw.html). – Marcos