11

私はPostgresをデータベースとして使用するRails3アプリを開発しています。以下に示す表があります。

             Table "public.test"
    Column     |  Type   | Modifiers
---------------+---------+-----------
 id            | integer | not null
 some_other_id | integer |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
    "some_other_id_key" UNIQUE CONSTRAINT, btree (some_other_id)

これには2つの列があります。

  • 主キーであるid(railsによって自動的に作成されます)
  • some_other_id。別のシステムによって生成されたキーが含まれています。このIDは一意である必要があるため、テーブルに一意のキー制約を追加しました。

ここで、重複する行を挿入しようとすると、some_other_id失敗し(良好)、Postgresログに次の出力が表示されます。

ERROR:  duplicate key value violates unique constraint "some_other_id_key"

問題は、アプリが同じIDを2回追加しようとすることが完全にメインラインであり、ログにこの「エラー」メッセージがスパムされていることです。これにより、さまざまな問題が発生します。ファイルに大量のディスクスペースが必要になり、診断が失われます。ノイズ、Postgresはログファイルをサイズ制限内に保つために診断を破棄する必要があります。

誰かが私がどのようにできるか知っていますか:

  • このキーに関するすべてのログを抑制するか、トランザクションで何かを指定することにより、ログを抑制しますINSERT
  • 他のPostgres機能を使用して、重複するキーを見つけ、を試さないでくださいINSERT。ルールとトリガーについて聞いたことがありますが、どちらも機能しません(Postgresの専門家ではありませんが)。

すべてのソリューションは、次のように挿入を行うRailsで動作する必要があることに注意してください。

INSERT INTO test (some_other_id) VALUES (123) RETURNING id;
4

3 に答える 3

11

重複キーエラーを回避するには、次のようにします。

INSERT INTO test (some_other_id)
SELECT 123
WHERE  NOT EXISTS (SELECT 1 FROM test WHERE some_other_id = 123)
RETURNING id;

idは、その値を自動的に取得 するシリアル列であると想定しています。

これは、非常に小さな競合状態SELECTの影響を受けます(との間のタイムスロットでINSERT)。しかし、発生する可能性のある最悪の事態は、結局のところ重複キーエラーが発生することであり、これはほとんど発生しないため、問題になることはありません。

フレームワークが適切な構文を使用するようにオプションを制限している場合は、いつでも生のSQLを使用できます。

または、次の目的でUDF(ユーザー定義関数)を作成することもできます。

CREATE FUNCTION f_my_insert(int)
 RETURNS int LANGUAGE SQL AS
$func$
INSERT INTO test (some_other_id)
SELECT $1
WHERE  NOT EXISTS (SELECT 1 FROM test WHERE some_other_id = $1)
RETURNING id;
$func$

電話:

SELECT f_my_insert(123);

または、デフォルトで既存のものに設定するにはid

CREATE FUNCTION f_my_insert(int)
 RETURNS int LANGUAGE plpgsql AS
$func$
BEGIN;

RETURN QUERY
SELECT id FROM test WHERE some_other_id = $1;

IF NOT FOUND THEN
   INSERT INTO test (some_other_id)
   VALUES ($1)
   RETURNING id;
END IF;

END
$func$

繰り返しますが、それは競合状態の可能性を最小限に抑えます。パフォーマンスが低下する代わりに、これを排除できます。

于 2012-09-12T12:47:04.420 に答える
3

セッションの(または実際にはグローバルに)エラーメッセージのログ記録を無効にすることができますが、スーパーユーザー権限が必要です。

実行することによって:

set log_min_messages=fatal;

setセッション(=接続)が終了するか、新しいステートメントを発行して値をリセットするまで、致命的なエラーのみがログに記録されます。

ただし、これを変更できるのはスーパーユーザーのみであるため、アプリケーションユーザーにその特権が必要になるため、セキュリティ上の大きな問題となるため、おそらく適切な解決策ではありません。

于 2012-09-12T10:18:19.253 に答える
0

での作業中にこれらのエラーを抑制したいだけの場合はpsql、次のことができます。

SET client_min_messages TO fatal

これはセッションの残りの間続きます。

于 2016-09-28T16:25:48.363 に答える