我的任务是同步位于不同数据库中的两个表。因此,对于源表中发生的每一次插入、更新和删除,这些更改都必须复制到目标表中。目标表将是源表的克隆。为了实现这一点,我决定在源表中安装触发器。
但我非常担心这些更新的并发方面,因为多个用户同时使用这些表,有时触发器必须更新目标表中的多行。从要在触发器中执行的更改的角度来看,我非常相信逻辑是正确的,但不是关于隔离级别,因为我不是这方面的专家。
因此,我将向您展示负责对目标表进行插入和更新的触发器,并请您查看并发方面是否存在任何问题。但在此之前,让我向您展示表格和一些用例:
这是源表(为简单起见,假设目标表具有相同的结构):
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
我想将 id 6 的父部门更改为指向 id 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 更改为指向 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;
将解决当前行的问题,但不能解决需要更新的其他行。
如果有人告诉我如何正确地纠正触发器以同时正常工作,我将非常高兴。
先感谢您。
马科斯