9

いくつかのフィールドにインデックスが付けられた PostgreSQL テーブルがあり、それらは重複を防ぐために一意である必要があります。これは、すべてのフィールドを挿入し、unique_violation 例外をキャッチする PLPGSQL 関数のおかげで行われますが、重複が 1 つしかない場合でもレコードの挿入を停止します。

パフォーマンスの問題により、いくつかの INSERT を作成できません (これらのいくつかは数百回行われます)。問題は、次の例の最初の 2 つの値のように、重複が 1 つだけであってもすべてのプロセスが停止することです。

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;

1 つのレコードに対してだけ 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

いいえ。

BEGIN...EXCEPTIONブロックはサブトランザクションであり、ブロック内のステートメントが失敗すると、サブトランザクションがロールバックされます。それはすべてか無かです。

cdhowieのアプローチを使用JOINVALUESます。

于 2012-07-26T00:43:58.117 に答える
2

探している動作を得るには、1 つのステートメントに複数の値を含めるのではなく、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 に答える