为什么查询在以前验证时会返回PK违规?

时间:2013-05-20 15:34:00

标签: postgresql postgresql-8.4

我有这张桌子

CREATE TABLE "UserCouponSentMail"
(
  "IdUser" integer NOT NULL,
  "IdCoupon" integer NOT NULL,
  "SendType" character varying(100),
  "Date" timestamp without time zone NOT NULL DEFAULT ('now'::text)::timestamp without time zone,
  CONSTRAINT "pk_UserCouponSentMail" PRIMARY KEY ("IdUser" , "IdCoupon" ),
  CONSTRAINT "pk_UserCouponSentMail_GroceryCoupon" FOREIGN KEY ("IdCoupon")
      REFERENCES "GroceryCoupon" ("IdGroceryCoupon") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH ( OIDS=FALSE );

和这个功能

CREATE OR REPLACE FUNCTION "UserCouponSentMailInsertOrUpdate"(integer, integer, character varying, timestamp without time zone)
  RETURNS void AS
$BODY$
BEGIN
    IF (EXISTS(SELECT * FROM "UserCouponSentMail" WHERE "IdUser" = $1 AND "IdCoupon" = $2)) THEN
        UPDATE "UserCouponSentMail" SET
            "SendType" = $3,
            "Date" = $4
        WHERE
            "IdUser" = $1 AND "IdCoupon" = $2;
    ELSE
        INSERT INTO "UserCouponSentMail" VALUES ($1, $2, $3, $4);
    END IF;
    RETURN;
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

我不知道为什么,但不知何故,我有时会在运行该功能时收到此错误

  

唯一违规:7错误:重复键值违反唯一约束“pk_UserCouponSentMail”语境:SQL语句“INSERT INTO”UserCouponSentMail“VALUES($ 1,$ 2,$ 3,$ 4)”PL / pgSQL函数“UserCouponSentMailInsertOrUpdate”第9行SQL声明

有关这怎么可能发生的任何想法?

两个脚本在同一时间运行此功能的可能性几乎是不可能的。

我在x86_64-redhat-linux-gnu上使用PostgreSQL 8.4.11,由GCC gcc(GCC)4.4.6 20110731(Red Hat 4.4.6-3)编译,64位。

感谢。

1 个答案:

答案 0 :(得分:1)

(IdUser,IdCoupon)具有相同值的并发执行似乎是唯一合理的解释。

问题是这几乎是不可能的,但多次执行并不总是有远见的。例如,在Web应用程序的上下文中,可能会出现具有完全相同数据的表单的双重帖子。

为了避免plpgsql级别的错误,您可以使用异常块:

BEGIN
  IF (EXISTS...) THEN
     UPDATE the unique corresponding row
  ELSE
     INSERT new row
  END IF;
EXCEPTION WHEN unique_violation THEN
  UPDATE the unique row
END;

此外,您可能希望在异常块中添加一些关于上下文和事件时间的日志,以期了解并发执行的原因。