繁体   English   中英

PostgreSQL:触发器函数绕过另一个表的触发器

[英]PostgreSQL: Trigger Function bypasses trigger of another table

有两个表:gc_res 和anchor。 锚表有一个唯一的键——anchor_id。

当数据到达 gc_res 时,res_eval 触发器被触发,数据被 gc_res_eval() 部分评估。 一些条目保留下来,其他条目通过插入到锚表中被推送。 锚表在插入之前有一个触发器,它也预先评估数据并接受插入或更新旧条目。 在后一种情况下,它可能会另外写入第三个(日志)表。

两个触发器看起来相同:

CREATE TRIGGER res_eval BEFORE INSERT ON gc_res
FOR EACH ROW EXECUTE PROCEDURE gc_res_eval();

CREATE TRIGGER anchor_smart_insert BEFORE INSERT ON anchor
FOR EACH ROW EXECUTE PROCEDURE anchor_smart_insert();

当我直接尝试写入锚表时,anchor_smart_insert 触发器正确触发并且工作正常。 尝试插入数据的 gc_res_eval 部分使用 CTE:

WITH anchor_insert AS (
    INSERT INTO cbm.anchor (anchor_id, pin, geo, rwo, rwo_value, addr) (
        SELECT osm_id, pin, geo, rwo, rwo_value, addr FROM gc_eval
        WHERE similarity > accept_polygon AND geo_type = 'Polygon'
    ) RETURNING anchor_id
) SELECT array_agg(anchor_id) FROM anchor_insert INTO anchors
;

当 gc_res_eval 尝试插入锚表并且 anchor_id 没有冲突时 - 一切正常。 如果存在冲突,则在唯一约束上插入失败。 因此我假设 - 第二个触发器不会触发。

CREATE OR REPLACE FUNCTION anchor_smart_insert()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
DECLARE
    geog geography;
    acceptable_intersect int := 10;
    acceptable_overlap_index NUMERIC := 0.56;
BEGIN
SELECT INTO geog geo FROM cbm.anchor a
    WHERE a.anchor_id=NEW.anchor_id;
RAISE NOTICE 'anchor_smart_insert is running'; 
IF ST_Area(ST_Intersection(geog, NEW.geo))>acceptable_intersect THEN
    UPDATE cbm.anchor a SET
        pin=COALESCE(NEW.pin, ST_Centroid(NEW.geo)),
        geo=NEW.geo,
        rwo=NEW.rwo,
        rwo_value=NEW.rwo_value,
        addr=NEW.addr
    WHERE a.anchor_id=NEW.anchor_id AND (
        a.geo IS DISTINCT FROM NEW.geo
        OR a.rwo IS DISTINCT FROM NEW.rwo
        OR a.rwo_value IS DISTINCT FROM NEW.rwo_value
        OR a.addr IS DISTINCT FROM NEW.addr);
    INSERT INTO cbm.anchor_history (anchor_id, geo) VALUES (NEW.anchor_id, geog);
    IF ST_Area(ST_Intersection(geog, NEW.geo))^2/(ST_Area(geog)*ST_Area(NEW.geo))<acceptable_overlap_index THEN
        INSERT INTO cbm.anchor_review(anchor_id, geo, rwo, rwo_value, addr) VALUES
            (NEW.anchor_id, NEW.geo, NEW.rwo, NEW.rwo_value, NEW.addr);
    END IF;
    RETURN NULL;
END IF;
RETURN NEW;
END;
$function$
;

DBMS 是一个 pg11 AWS RDS。

责备自己:只要比较的多边形大于 10 平方米,anchor_smart_insert() 函数就可以正常工作。 有一个假设,应该只有两个选择:大于 10 平方米,或者空。

根据这样的逻辑推理,删除了包含所有豁免的部分,因为它似乎没有必要。

这也是为什么通过直接插入进行的测试效果很好:多边形大于 10 平方米并导致更新。

谢谢@Adrian Klaver - 您的建议有帮助!

恢复/改进的功能以某种方式正确处理 <10 平方米的多边形,它将其与初始和最终尺寸进行预先比较 - 如果它们更小,则需要更安全的值。 此外,它还明确处理可能的插入(现有的 anchor_id ISNULL)。

CREATE OR REPLACE FUNCTION cbm.anchor_smart_insert()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
DECLARE
    geog geography;
    acceptable_intersect NUMERIC:= 10;
    acceptable_overlap_index NUMERIC := 0.56;
BEGIN
SELECT INTO geog geo FROM cbm.anchor a
    WHERE a.anchor_id=NEW.anchor_id;
IF geog ISNULL THEN RETURN NEW; END IF;
IF ST_Area(ST_Intersection(geog, NEW.geo))>LEAST(acceptable_intersect, GREATEST(ST_Area(geog), ST_Area(NEW.geo))) THEN
    UPDATE cbm.anchor a SET
        pin=COALESCE(NEW.pin, ST_Centroid(NEW.geo)),
        geo=NEW.geo,
        rwo=NEW.rwo,
        rwo_value=NEW.rwo_value,
        addr=NEW.addr
    WHERE a.anchor_id=NEW.anchor_id AND (
        a.geo IS DISTINCT FROM NEW.geo
        OR a.rwo IS DISTINCT FROM NEW.rwo
        OR a.rwo_value IS DISTINCT FROM NEW.rwo_value
        OR a.addr IS DISTINCT FROM NEW.addr);
    IF ST_Area(ST_Intersection(geog, NEW.geo))^2/(ST_Area(geog)*ST_Area(NEW.geo))<acceptable_overlap_index THEN 
        INSERT INTO cbm.anchor_history (anchor_id, geo) VALUES (NEW.anchor_id, geog);
    END IF;
END IF;
-- bad intersection:
INSERT INTO cbm.anchor_review(anchor_id, geo, rwo, rwo_value, addr) VALUES
    (NEW.anchor_id, NEW.geo, NEW.rwo, NEW.rwo_value, NEW.addr);
RETURN NULL;
END;
$function$
;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM