使用异常处理来修复PL / pgsql中的外键约束(PostgreSQL)

时间:2015-05-12 03:32:03

标签: postgresql plpgsql

尝试学习pgSQL中的异常处理(PostgreSQL 9.1)。以下SP失败并带有

ERROR: insert or update on table "dx" violates foreign key constraint "fk_icd9"
SQL state: 23503
Detail: Key (cicd9, cdesc)=(244.9, testing1) is not present in table "icd9".

fk_icd9从表dx定义为:

CONSTRAINT fk_icd9 FOREIGN KEY (cicd9, cdesc)
  REFERENCES icd9 (cicd9, cdesc) MATCH SIMPLE
  ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED

我对SP的尝试是:

CREATE OR REPLACE FUNCTION g_test() RETURNS void AS $$
DECLARE
    r View_dx%rowtype;
BEGIN
  r.cicd9 := '244.9';
  r.groupid := 'BBBB      CCCC        199971230';  
  r.tposted := '2013-08-30 17:45:45'::timestamp;
  r.cdesc := 'testing1';

  LOOP
    BEGIN
      UPDATE dx SET cdesc = r.cdesc
      WHERE cicd9 = r.cicd9 AND groupid = r.groupid AND tposted = r.tposted;
    EXCEPTION 
      WHEN others THEN
        INSERT INTO icd9(cicd9, cdesc) VALUES (r.cicd9, r.cdesc);
    END;
    IF FOUND THEN
      RETURN;
    END IF; 
  END LOOP;
END; $$ LANGUAGE plpgsql;

我正在尝试更新第二个表icd9中具有外键约束的表dx。如果由于此约束而导致dx表更新失败,那么我想在父icd9表中插入新记录,然后循环回第一个表dx进行更新。

我做错了什么?这是怎么做到的?

编辑#1:编辑如下所示的代码:

 create or replace function g_savedx3() returns void as
$$
 DECLARE

_cicd9 character varying(8);
_groupid character varying(33);
_tposted timestamp without time zone;
_cdesc character varying(80); 


BEGIN
_cicd9 := '244.9';
_groupid := 'BBBBB        AAAAA        199998';  
_tposted := '2013-08-30 17:45:45'::timestamp;
_cdesc := 'testing109';

LOOP
    BEGIN
        RAISE NOTICE 'About to update ';

        UPDATE dx SET cdesc = _cdesc
            WHERE 
                cicd9 = _cicd9 and 
                groupid = _groupid and tposted = _tposted;

        RAISE NOTICE 'Updated in g_saveDx3';

        IF FOUND THEN
            RETURN;
        END IF;


        EXCEPTION 
            WHEN others THEN
                RAISE NOTICE 'In exception g_saveDx3, about to insert';

                INSERT INTO icd9(cicd9,cdesc) VALUES (_cicd9, _cdesc);

                RAISE NOTICE 'In exception inserted';
    END;
END LOOP;
END;
$$
LANGUAGE plpgsql;

select g_savedx3();

收到以下消息:

注意:即将更新 注意:已在g_saveDx3

中更新

错误:在表“dx”上插入或更新违反外键约束“fk_icd9” 详细信息:表“icd9”中不存在键(cicd9,cdesc)=(244.9,testing109)。 **********错误**********

错误:在表“dx”上插入或更新违反外键约束“fk_icd9” SQL状态:23503 细节:表“icd9”中没有键(cicd9,cdesc)=(244.9,testing109)。

注意:我在Tom {(2004)

updates violating foreign constraints找到了一个旧条目
  

是的......你期待RI触发器在此期间触发   plpgsql函数,但实际上它们在外部完成时会触发   调用plpgsql函数的语句。

     

关于这是否真的最重要,一直存在争议   理想的行为,但现在就是这样。

如果仍然如此,它可能会解释问题。任何想法如何修复我的代码? (应该有用吗?)谢谢。

***如下所述,我猜这是默认行为,在我的情况下,导致在完成plpgsql函数后调用异常。可以使用以下命令更改此行为(PostgreSQL 9.1)。

  

SET CONSTRAINTS ALL IMMEDIATE;

需要做出这项工作

如果它有任何方位,这里是ICD9表的定义:

 CREATE TABLE icd9
(
 recid serial NOT NULL,
 cicd9 character varying(8),
 cdesc character varying(80) NOT NULL,
 "timestamp" timestamp without time zone DEFAULT now(),
 modified timestamp without time zone DEFAULT now(),
 chronic boolean NOT NULL DEFAULT false,
 CONSTRAINT pk_icd9_recid PRIMARY KEY (recid),
 CONSTRAINT constraint_cdesc UNIQUE (cicd9, cdesc),
 CONSTRAINT desccheck CHECK (cdesc::text <> ''::text)
)
WITH (
 OIDS=FALSE
);

2 个答案:

答案 0 :(得分:2)

在你的循环中你有

IF FOUND THEN
  RETURN;
END IF;

这会在循环进入INSERT后的下一次迭代之前结束该函数,因为该命令也设置了FOUND

你想要的是:

LOOP
  BEGIN
    UPDATE dx SET cdesc = r.cdesc
    WHERE cicd9 = r.cicd9 AND groupid = r.groupid AND tposted = r.tposted;
    IF FOUND THEN
      RETURN;
    END IF;
  EXCEPTION 
    WHEN others THEN
      INSERT INTO icd9(cicd9, cdesc) VALUES (r.cicd9, r.cdesc);
  END;
END LOOP;

答案 1 :(得分:1)

切换到老式的调试。这是我的代码,它确实插入。

create or replace function f () returns void as

$$
DECLARE
newval integer :=3 ;
BEGIN
LOOP
BEGIN
    RAISE NOTICE 'About to update ';
    UPDATE B SET ID2 = newval;
    RAISE NOTICE 'Updated ';
    IF FOUND THEN
      RETURN;
    END IF;
EXCEPTION 
    WHEN others THEN
    RAISE NOTICE 'In exception , about to insert';
      INSERT INTO a VALUES (newval);
RAISE NOTICE 'In exception inserted';
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;

执行:

select f();
NOTICE:  About to update 
NOTICE:  In exception , about to insert
NOTICE:  In exception inserted
NOTICE:  About to update 
NOTICE:  Updated 

表定义:

test=# \d+ a
                           Table "w2gi.a"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer | not null  | plain   |              | 
Indexes:
    "a_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "b" CONSTRAINT "b_id2_fkey" FOREIGN KEY (id2) REFERENCES a(id)
Has OIDs: no
test=# \d+ b
                           Table "w2gi.b"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id1    | integer |           | plain   |              | 
 id2    | integer |           | plain   |              | 
Foreign-key constraints:
    "b_id2_fkey" FOREIGN KEY (id2) REFERENCES a(id)
Has OIDs: no