9

我有一个 PostgreSQL 表,其中包含一些索引字段,并且这些字段必须是唯一的以防止重复。这要归功于插入所有字段并捕获 unique_violation 异常的 PLPGSQL 函数,尽管即使只有一个重复,它也会停止插入记录。

由于性能问题,我无法进行多次插入(其中一些以数百次完成),问题是即使只有一个重复,它也会停止所有过程,就像下面示例中最火的两个值一样。

CREATE OR REPLACE FUNCTION easy_import() RETURNS VOID AS
  $$
  BEGIN
    BEGIN
      INSERT INTO things ("title", "uniq1", "uniq2") VALUES
      ('title 1', 100, 102),
      ('title 2', 100, 102),
      ('title 3', 101, 102),
      ('title 4', 102, 102),
      ('title 5', 103, 102),
      ('title 6', 104, 102),
      ('title 7', 105, 102),
      ('title 8', 106, 102),
      ('title 9', 107, 102),
      ('title 10', 108, 102);
      RETURN;
    EXCEPTION WHEN unique_violation THEN
      -- do nothing
    END;
  END;
  $$
  LANGUAGE plpgsql;

有没有办法只为一条记录忽略 unique_violation 并阻止它停止进一步的 INSERT?

谢谢你。

更新

  • 唯一索引在“uniq1”和“uniq2”字段上有它,我很抱歉造成混淆。
  • 虽然@cdhowie 的解决方案似乎是最好的,但它以某种方式忽略了这样一个事实,即如果您运行相同的查询,它将触发错误。这很奇怪,因为查询是JOIN有原因的。仍在努力。
4

4 回答 4

6

假设唯一约束是围绕 uniq1 和 uniq2 复合的,这将起作用:

INSERT INTO things

WITH new_rows (title, uniq1, uniq2) AS (VALUES
    ('title 1', 100, 102),
    ('title 2', 100, 102),
    ('title 3', 101, 102),
    ('title 4', 102, 102),
    ('title 5', 103, 102),
    ('title 6', 104, 102),
    ('title 7', 105, 102),
    ('title 8', 106, 102),
    ('title 9', 107, 102),
    ('title 10', 108, 102)
)

SELECT
    DISTINCT ON (n.uniq1, n.uniq2)
    n.title, n.uniq1, n.uniq2

FROM new_rows AS n

LEFT JOIN things AS t
ON n.uniq1 = t.uniq1 AND n.uniq2 = t.uniq2

WHERE t.uniq1 IS NULL;

这实际上可能最终不如单个 INSERT 语句的性能,但它是唯一可以解决问题的事情。对每种方法进行基准测试,看看哪种方法最适合您。

于 2012-07-25T23:15:35.107 回答
3

你的桌子是这样的:

CREATE TABLE t
(
  title text,
  uniq1 int not null,
  uniq2 int nut null,
  CONSTRAINT t_pk_u1_u2 PRIMARY KEY (uniq1,uniq2)
)

所以让我添加一条规则:

CREATE OR REPLACE RULE ignore_duplicate_inserts_on_t AS ON INSERT TO t
   WHERE (EXISTS ( SELECT 1 FROM t WHERE t.uniq1 = new.uniq1 and t.uniq2 = new.uniq2))
   DO INSTEAD NOTHING;

之后,您可以运行此查询:

insert into t(title,uniq1,uniq2) values 
    ('title 1', 100, 102),
    ('title 2', 100, 102),
    ...;

如果你的桌子很大,这种方式是最佳的。我在大约 200 万行的表上进行了测试(对于这种方式和上面 cdhowie 先生提到的连接方式),结果是:

Rule way (mentioned in this comment): 1400 rows per second
Join way (mentioned in above comment): 650 rows per second
于 2013-08-13T11:40:07.350 回答
2

要获得您正在寻找的行为,只需将 INSERT 语句拆分为单独的语句,而不是在一个语句中包含多个值。与您的原始版本相比,性能应该没有任何明显的差异,因为一切仍然在一个事务中。

CREATE OR REPLACE FUNCTION easy_import() RETURNS VOID AS
$$
BEGIN
BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 1', 100, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 2', 100, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 3', 101, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 4', 102, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 5', 103, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 6', 104, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 7', 105, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 8', 106, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 9', 107, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

BEGIN
  INSERT INTO things ("title", "uniq1", "uniq2") VALUES ('title 10', 108, 102);
EXCEPTION WHEN unique_violation THEN
 -- do nothing
END;

RETURN;
END;
$$
LANGUAGE plpgsql;
于 2012-07-26T13:35:24.960 回答
2

没有。

BEGIN...块是子事务,当EXCEPTION块内的语句失败时,子事务将回滚。要么全有,要么全无。

使用 cdhowie 的方法 -JOINVALUES.

于 2012-07-26T00:43:58.117 回答