0

我有这张桌子

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”值($1、$2、$3、$4)”PL/pgSQL 函数“UserCouponSentMailInsertOrUpdate”SQL 语句第 9 行

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

两个脚本同时使用相同参数运行此函数的可能性几乎是不可能的。

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

谢谢。

4

1 回答 1

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;

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

于 2013-05-20T16:23:13.243 回答