Postgresql函数不处理重复记录

时间:2013-07-22 10:06:16

标签: postgresql plpgsql

更新:在对我的代码进行大量检测之后,我找到了另一个开发人员在不使用'upsert'功能的情况下插入此表的位置。但是,由于方法的分组方式和捕获的异常,堆栈跟踪表明此函数出错,实际上,调用该函数是错误。下面的代码很好(丹尼尔添加了警告)。

我有以下plpgsql函数:

-- http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/
CREATE OR REPLACE FUNCTION upsert_person_site(
    curr_site_id   INTEGER,
    curr_person_id INTEGER,
    curr_job_title CHARACTER VARYING(128)
) RETURNS void as $$
BEGIN
    -- strictly speaking, running the update first is not needed and
    -- duplicate code, but exceptions are relatively expensive.
    -- Also, note that we refuse to update with a NULL job title because an
    -- import may simply fail to specify one.
    UPDATE person_site
       SET job_title = curr_job_title
     WHERE site_id   = curr_site_id
       AND person_id = curr_person_id
       AND curr_job_title IS NOT NULL;
    IF FOUND THEN
        RETURN;
    END IF;
    BEGIN
        INSERT INTO person_site (      site_id,      person_id,      job_title )
                         VALUES ( curr_site_id, curr_person_id, curr_job_title );
    EXCEPTION WHEN OTHERS THEN
        UPDATE person_site
           SET job_title = curr_job_title
         WHERE site_id   = curr_site_id
           AND person_id = curr_person_id
           AND curr_job_title IS NOT NULL;
    END;
    RETURN;
END;
$$ language plpgsql;

目的是在记录存在时进行更新,如果不存在则进行插入。预期的逻辑是:

// duplicating the update to avoid trapping an expensive exception
try to update the record
if successful
    return

try
    // I believe it's failing here
    insert a new record
catch
    update an existing record

person_site表在person_site_pkeyperson_id字段中有一个site_id。但是,很多时候运行此函数时,我会收到一个说明duplicate key value violates unique constraint "person_site_pkey" at ...的异常。

有人能帮我理解我错过的东西吗?我以为EXCEPTION WHEN OTHERS块会陷阱。

这是在Debian Squeezebox上运行的Postgresql 8.4.13。应用程序代码是Perl并使用DBD::Pg模块。

1 个答案:

答案 0 :(得分:1)

curr_job_title为NULL时,无论此人是否已经在表中,代码都会进入INSERT。

基于此评论:

-- Also, note that we refuse to update with a NULL job title because an
-- import may simply fail to specify one.

测试应更好地实施为:

IF curr_job_title IS NULL THEN
  return;
END IF;

在尝试第一次UPDATE之前的函数开头。