我可以在FOR循环中使用EXCEPTION来在(可预测或不可预测)错误发生时强制继续吗?这是一个片段:
FOR temp_rec IN tlcursor LOOP
tl2 := temp_rec; --the location to be updated
--Do the Routing and UPDATE the taxilocs row.
UPDATE taxilocs20120113
SET route = pgr_trsp (
'SELECT * FROM th_2po_4pgr',
tl1.map_id, tl1.map_pos, tl2.map_id, tl2.map_pos, false, true);
tl1 := tl2;
END LOOP;
一个例子是,在第一次迭代期间,未分配tl1
。另一个可能是函数pge_trsp()
没有提供任何结果。如果在执行期间发生任何此类情况,我可以强制循环继续吗?
的更新!!
这是我最新的代码:
CREATE OR REPLACE FUNCTION fm_seqrouting() RETURNS integer AS $$
--Declarations
DECLARE
tlcursor SCROLL CURSOR FOR SELECT
oid,
veh_id,
date_time AS dt,
th_2po_4pgr_id AS map_id,
th_2po_4pgr_position AS map_pos FROM testlocs
ORDER BY veh_id, dt;
tl1 RECORD;
tl2 RECORD;
i integer;
BEGIN
--CODE to calculate routes and update table
i := 0;
FOR temp_rec IN tlcursor LOOP
tl2 := temp_rec; --the location to be updated
BEGIN --Nest the UPDATE in a block to esace errors
--Do the Routing and UPDATE the taxilocs row.
UPDATE taxilocs20120113
SET "pgRoute" = pgr_trsp (
'SELECT * FROM th_2po_4pgr',
tl1.map_id, tl1.map_pos, tl2.map_id, tl2.map_pos, false, true)
WHERE taxilocs20120113.oid = tl2.oid;
i := i + 1;
EXCEPTION
WHEN SQLSTATE '55000' THEN NULL;
WHEN SQLSTATE 'XX000' THEN NULL;
WHEN SQLSTATE '38001' THEN NULL;
END;
tl1 := tl2;
END LOOP;
RETURN i;
END;
$$ LANGUAGE plpgsql;
事实是,它在记忆方面非常紧张,但我是编程新手(我甚至不是程序员)。任何提示赞赏!
答案 0 :(得分:4)
是。您可以将有效负载放在具有异常处理的单独代码块中:
FOR temp_rec IN tlcursor LOOP
tl2 := temp_rec; --the location to be updated
--Do the Routing and UPDATE the taxilocs row.
BEGIN
UPDATE taxilocs20120113
SET route = pgr_trsp (
'SELECT * FROM th_2po_4pgr',
tl1.map_id, tl1.map_pos, tl2.map_id, tl2.map_pos, false, true);
EXCEPTION WHEN OTHERS THEN
-- keep looping
END;
tl1 := tl2;
END LOOP;
但我不明白为什么你首先分配tl2
(而不是tl1
),这必然会在循环的第一次迭代中导致异常。您可以使用FOR
loop而不是显式游标与增强查询相结合来避免先验问题。见下文。
此外,您的UPDATE
没有WHERE
条件,这几乎肯定是错误的。
至少可以说功能pgr_trsp()
看起来很可疑。传递代码作为SQL注入的文本reeks。关于dba.SE的相关答案在plpgsql中评估了SQLi:
Postgres functions vs prepared queries
重写代码以使用基于集合的逻辑而不是循环可能更清晰,更快捷。 对于启动器,您可以简化为类似的东西(仍然使用循环,但简化):
CREATE OR REPLACE FUNCTION fm_seqrouting()
RETURNS integer AS
$func$
DECLARE
r record;
BEGIN
FOR r IN
SELECT oid -- no proper pk?
,th_2po_4pgr_id AS map_id1
,th_2po_4pgr_position AS map_pos1
,lead(th_2po_4pgr_id) OVER w AS map_id2
,lead(th_2po_4pgr_position) OVER w AS map_pos2
,count(*) OVER () AS ct
FROM testlocs
WINDOW w AS (ORDER BY veh_id, dt)
ORDER BY veh_id, dt -- you don't need order by columns in result
LOOP
BEGIN -- may be unnecessary
UPDATE taxilocs20120113
SET "pgRoute" = pgr_trsp(
'SELECT * FROM th_2po_4pgr'
,r.last_map_id, r.last_map_pos, r.map_id, r.map_pos, false, true)
WHERE taxilocs20120113.oid = r.oid;
EXCEPTION
WHEN SQLSTATE '55000' THEN NULL;
WHEN SQLSTATE 'XX000' THEN NULL;
WHEN SQLSTATE '38001' THEN NULL;
END;
END LOOP;
RETURN r.ct;
END
$func$ LANGUAGE plpgsql;
特别是,使用......
FOR
loop with implicit cursor而不是一个(笨重的)显式游标。答案 1 :(得分:2)
正如Erwin所示,你可以这样做,但效率很低。 PostgreSQL必须为每个BEGIN ... EXCEPTION
块创建一个新的子事务,它会烧掉事务ID,并且意味着需要更快,更频繁地需要反环绕真空。它还会使内部事务ID数组膨胀,从而降低所有后端速度,并增加进程间通信成本。
如果可能的话,您应该编写代码以避免错误。当尝试替换PL / pgSQL循环时,递归CTE和可写CTE通常很有用。
如果没有支持tlcursor
的实际查询,则很难将其重新命名为查询。我怀疑有可能将其改写为UPDATE ... FROM
,但如果行n
依赖于行n-1
,那么这将不起作用,在这种情况下它看起来可能是行tlcursor
。递归CTE适用于该情况。
如果您编辑问题以显示示例数据,{{1}}查询的定义等,那么在此处发表评论我可能会在编写CTE时更换它。