该函数在循环之前被卡住.
select * from scm_main.fn_connection_stations(1219646)
打印消息“开始…”但不打印消息“…结束”.
CREATE OR REPLACE FUNCTION scm_main.fn_connection_stations(var_connection_id bigint) RETURNS SETOF scm_main.typ_connection_stations AS $BODY$DECLARE var_affected INTEGER DEFAULT 0; var_row scm_main.typ_connection_stations%ROWTYPE; BEGIN RAISE NOTICE 'Start...'; FOR var_row IN SELECT DISTINCT v.vbvdata_station_id FROM scm_main.tbl_vbvdata AS v INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id and p.packet_connection_id = var_connection_id) --WHERE v.vbvdata_packet_id IN --( SELECT packet_id FROM scm_main.tbl_packet AS o_p WHERE o_p.packet_connection_id = var_connection_id) LOOP RETURN NEXT var_row; END LOOP; RAISE NOTICE '...End'; RETURN; END $BODY$ LANGUAGE plpgsql STABLE STRICT COST 100 ROWS 1000; ALTER FUNCTION scm_main.fn_connection_stations(bigint) OWNER TO postgres;
SELECT DISTINCT v.vbvdata_station_id FROM scm_main.tbl_vbvdata AS v INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id and p.packet_connection_id = 1219646)
我想问题必须是桌子的锁定.但我绝对不明白是什么原因以及可以解决的问题.
前段时间评论部分正在运作.但过了一会儿,出现了同样的问题.我通过更改查询并用内部联接替换条件(级联查询)来解决它.但这次他们都没有工作!
更新
我通过一个愚蠢的改变再次使它工作:
INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id and p.packet_connection_id = var_connection_id)`
改为:
INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id) where p.packet_connection_id in (select var_connection_id)
并且工作得很好.
另一个有趣的观点是,即使是下面的更改也无法正常工作并仍然挂起:
INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id) where p.packet_connection_id = var_connection_id
现在我找到了解决方案.但我想知道它是怎么发生的!
解决方法
如果通过对查询的这种表面改变来修复它,那么它可能不会“卡住”,而是由于选择不当的执行计划而非常缓慢地运行.
查询在函数中的性能比它本身更糟的事实可能是plan caching的结果.基本上,Postgres可能会尝试通过创建和重用泛型(即参数独立)来避免重新规划查询的成本执行计划.不幸的是,这些通用计划可能远非最佳.
坏计划的一个可能解释是糟糕的统计数据;如果Postgres没有关于数据的准确信息,则必然会做出糟糕的决定.对所涉及的表进行分析可能有所帮助,但通常不需要这样做 – autovacuum通常应该使统计数据保持最新(假设您已运行).
统计偏差的一个常见原因是值的分布不均匀(在您的情况下,如果每个连接的数据包数量有很大差异).通过使用ALTER TABLE ... SET STATISTICS
(后跟ANALYZE)提高统计信息的详细程度,可以大大改善这一点.在某些情况下,高值会减慢计划,但500(可能)是一个安全的起点.
如果没有其他工作,您可以通过EXECUTE运行查询来绕过计划缓存.