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

我想将 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;

将解决当前行的问题,但不能解决需要更新的其他行。

如果有人告诉我如何正确地纠正触发器以同时正常工作,我将非常高兴。

先感谢您。

马科斯

4

1 回答 1

0

也许使用事务可能会有所帮助……据我所知,它们会阻止数据库上的所有修改,直到所有操作完成,因此它们会立即提交更改以避免不一致的更新。查看交易,它可能是您正在寻找的

于 2015-03-24T09:36:46.570 回答