database - Postgres 触发执行速度的差异?

标签 database postgresql triggers plpgsql

我有一个触发器可以在表插入或更新时执行函数。它看起来像这样:

CREATE OR REPLACE FUNCTION func_fk_location_area()
RETURNS "trigger" AS $$
BEGIN
    IF EXISTS (
        -- there was a row valid in area when location started
        SELECT * FROM location
        WHERE NOT EXISTS (
            SELECT * FROM area
             WHERE area.key=location.key
               AND area.id=location.area_id
               AND (  (area.tr_from<=location.tr_from AND area.tr_until>location.tr_from) OR
                      (area.tr_from=location.tr_from AND area.tr_until=location.tr_from)))
    ) OR EXISTS (
        -- there was a row valid in area when location ended
        SELECT * FROM location
        WHERE NOT EXISTS (
            SELECT * FROM area
             WHERE area.key=location.key
               AND area.id=location.area_id
               AND (  (area.tr_from<location.tr_until AND area.tr_until>=location.tr_until) OR
                      (area.tr_from=location.tr_until AND area.tr_until=location.tr_until)))
    )
    THEN
        RAISE EXCEPTION 'FK location_area integrity violation.';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_fk_area_location AFTER DELETE OR UPDATE ON area
    FOR EACH ROW EXECUTE PROCEDURE func_fk_location_area();
CREATE TRIGGER trigger_fk_location_area AFTER INSERT OR UPDATE ON location
    FOR EACH ROW EXECUTE PROCEDURE func_fk_location_area();

当我插入一行时,它似乎运行得很慢。使用 explain analyze,我确定此触发器需要将近 400 毫秒才能完成。

 Result  (cost=0.00..0.03 rows=1 width=0) (actual time=0.026..0.029 rows=1 loops=1)
 Trigger for constraint location_fkey_tr_by: time=0.063 calls=1
 Trigger trigger_fk_location_area: time=361.878 calls=1
 Trigger trigger_update_objects_location: time=355.033 calls=1
 Total runtime: 717.229 ms
(5 rows)

但是,如果我在函数中运行两批 SQL,它们每条只需要 3 或 4 毫秒即可运行!

第一部分:

mydb=# explain analyze
mydb-#             SELECT * FROM location
mydb-#             WHERE NOT EXISTS (
mydb(#                 SELECT * FROM area
mydb(#                  WHERE area.key=location.key
mydb(#                    AND area.id=location.area_id
mydb(#                    AND (  (area.tr_from<location.tr_until AND area.tr_until>=location.tr_until) OR
mydb(#                           (area.tr_from=location.tr_until AND area.tr_until=location.tr_until)));

 Hash Anti Join  (cost=14.68..146.84 rows=1754 width=126) (actual time=5.512..5.512 rows=0 loops=1)
   Hash Cond: ((location.key = area.key) AND (location.area_id = area.id))
   Join Filter: (((area.tr_from < location.tr_until) AND (area.tr_until >= location.tr_until)) OR ((area.tr_from = location.tr_until) AND (area.tr_until = locat
ion.tr_until)))
   ->  Seq Scan on location  (cost=0.00..79.91 rows=2391 width=126) (actual time=0.005..1.016 rows=2393 loops=1)
   ->  Hash  (cost=8.87..8.87 rows=387 width=37) (actual time=0.497..0.497 rows=387 loops=1)
         ->  Seq Scan on area  (cost=0.00..8.87 rows=387 width=37) (actual time=0.004..0.250 rows=387 loops=1)
 Total runtime: 5.562 ms
(7 rows)

第二部分:

mydb=# explain analyze
mydb-#             SELECT * FROM location
mydb-#             WHERE NOT EXISTS (
mydb(#                 SELECT * FROM area
mydb(#                  WHERE area.key=location.key
mydb(#                    AND area.id=location.area_id
mydb(#                    AND (  (area.tr_from<location.tr_until AND area.tr_until>=location.tr_until) OR
mydb(#                           (area.tr_from=location.tr_until AND area.tr_until=location.tr_until)));

 Hash Anti Join  (cost=14.68..146.84 rows=1754 width=126) (actual time=5.666..5.666 rows=0 loops=1)
   Hash Cond: ((location.key = area.key) AND (location.area_id = area.id))
   Join Filter: (((area.tr_from < location.tr_until) AND (area.tr_until >= location.tr_until)) OR ((area.tr_from = location.tr_until) AND (area.tr_until = locat
ion.tr_until)))
   ->  Seq Scan on location  (cost=0.00..79.91 rows=2391 width=126) (actual time=0.005..1.072 rows=2393 loops=1)
   ->  Hash  (cost=8.87..8.87 rows=387 width=37) (actual time=0.509..0.509 rows=387 loops=1)
         ->  Seq Scan on area  (cost=0.00..8.87 rows=387 width=37) (actual time=0.007..0.239 rows=387 loops=1)
 Total runtime: 5.725 ms
(7 rows)

这对我来说毫无意义。

有什么想法吗?

谢谢。

最佳答案

您正在设置针对每一行运行的触发器,然后在触发器函数内对整个表进行另一个选择。做一个或另一个。 (尝试将 FOR EACH ROW 更改为 FOR EACH STATEMENT。)

关于database - Postgres 触发执行速度的差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3934213/

上一篇:mysql - sql查询构建

下一篇:sql使用别名

相关文章:

MySQL 插入后触发器 "Unknown Column"

php - 如何更新mysql数据库中存储的数据

mysql - 在 Mysql 中使用 join、sum 和 group by

oracle - 在 "having"子句中从 Postgres 中的 Oracle 转换 rownum

javascript - jQuery 添加事件并立即执行

sql - Oracle数据库-确定哪个表触发了触发器

mysql - 如何关闭 MySQL 中的现有连接

database - Cassandra 顺序和聚类键

javascript - postman 没有收到 POST 的回复

php - 启用 PHP APC 查询缓存