我可以在FOR LOOP中使用EXCEPTION来强制继续出错吗?

时间:2013-12-02 10:12:36

标签: postgresql loops exception-handling plpgsql

我可以在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;  

事实是,它在记忆方面非常紧张,但我是编程新手(我甚至不是程序员)。任何提示赞赏!

2 个答案:

答案 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;

example in the manual

但我不明白为什么你首先分配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;

特别是,使用......

答案 1 :(得分:2)

正如Erwin所示,你可以这样做,但效率很低。 PostgreSQL必须为每个BEGIN ... EXCEPTION块创建一个新的子事务,它会烧掉事务ID,并且意味着需要更快,更频繁地需要反环绕真空。它还会使内部事务ID数组膨胀,从而降低所有后端速度,并增加进程间通信成本。

如果可能的话,您应该编写代码以避免错误。当尝试替换PL / pgSQL循环时,递归CTE和可写CTE通常很有用。

如果没有支持tlcursor的实际查询,则很难将其重新命名为查询。我怀疑有可能将其改写为UPDATE ... FROM,但如果行n依赖于行n-1,那么这将不起作用,在这种情况下它看起来可能是行tlcursor。递归CTE适用于该情况。

如果您编辑问题以显示示例数据,{{1}}查询的定义等,那么在此处发表评论我可能会在编写CTE时更换它。