Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 一致更新表中的多行,避免并发干扰_Sql_Postgresql_Plpgsql_Isolation Level_Transaction Isolation - Fatal编程技术网

Sql 一致更新表中的多行,避免并发干扰

Sql 一致更新表中的多行,避免并发干扰,sql,postgresql,plpgsql,isolation-level,transaction-isolation,Sql,Postgresql,Plpgsql,Isolation Level,Transaction Isolation,我的任务是同步位于不同数据库中的两个表。因此,对于源表中发生的每一次插入、更新和删除,都必须在目标表中复制这些更改。目标表将是源表的克隆。为了实现这一点,我决定在源表中安装触发器 但我非常关心这些更新的并发方面,因为多个用户同时使用表,有时触发器必须更新目标表中的多行。从触发器中要执行的更改的角度来看,我非常确信逻辑是正确的,但隔离级别不是这样,因为我不是这方面的专家 因此,我将向您展示对目标表上的插入和更新负责的触发器,并要求您查看并发方面是否存在任何问题。但在此之前,让我向您展示表格和一些用

我的任务是同步位于不同数据库中的两个表。因此,对于源表中发生的每一次插入、更新和删除,都必须在目标表中复制这些更改。目标表将是源表的克隆。为了实现这一点,我决定在源表中安装触发器

但我非常关心这些更新的并发方面,因为多个用户同时使用表,有时触发器必须更新目标表中的多行。从触发器中要执行的更改的角度来看,我非常确信逻辑是正确的,但隔离级别不是这样,因为我不是这方面的专家

因此,我将向您展示对目标表上的插入和更新负责的触发器,并要求您查看并发方面是否存在任何问题。但在此之前,让我向您展示表格和一些用例:

为简单起见,这是源表,假设目标表具有相同的结构:

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
因此,正如您所看到的,只有一行受到更新的影响。现在假设我想将ID1的父id更改为NULL,以指向原始行集中的ID6。因此,更改后,您将有:

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

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

两个位于不同数据库中的表。不同的数据库是一个事务的一部分吗?您至少需要两个阶段的提交,这两个阶段实际上是两个同步的transactions@joop使用postgres_fdw[link],您可以使位于不同数据库中的两个表共享同一事务。这就是我用的。触发器已在某个事务下运行。@joop更具体地说,请参见第F.31.3节。中的事务管理。触发器已在由应用程序代码启动的事务下运行,但事务不会将您与并发问题(如脏读、不可重复读等)隔离开来。您必须正确编写查询和更新代码以避免这些问题。这就是我在这里寻找的。如果有人能指出上述触发器中的任何缺陷,我将非常感激。