0

次のトリガーを作成して、挿入のフィールド 'filesequence' が 1 人の利害関係者に対して常に最大値 + 1 を受け取ることを保証しました。

CREATE OR REPLACE FUNCTION update_filesequence()
        RETURNS TRIGGER AS '
  DECLARE
    lastSequence file.filesequence%TYPE;

  BEGIN
    IF (NEW.filesequence IS NULL) THEN

      PERFORM ''SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE'';

      SELECT max(filesequence) INTO lastSequence FROM file WHERE stakeholder = NEW.stakeholder;

      IF (lastSequence IS NULL) THEN
        lastSequence = 0;
      END IF;

      lastSequence = lastSequence + 1;

      NEW.filesequence = lastSequence;
    END IF;
    RETURN NEW;
  END;
' LANGUAGE 'plpgsql';

CREATE TRIGGER file_update_filesequence BEFORE INSERT
  ON file FOR EACH ROW EXECUTE PROCEDURE
  update_filesequence();

しかし、データベースで「filesequence」を繰り返しました:

select id, filesequence, stakeholder from file where stakeholder=5273;
id      filesequence    stakeholder
6773    5               5273
6774    5               5273

私の理解では、SELECT... FOR UPDATE は同じ利害関係者の 2 つのトランザクションをロックし、2 番目のトランザクションは新しい「ファイルシーケンス」を読み取ります。しかし、それは機能していません。

PgAdmin でいくつかのテストを行い、以下を実行しました。

BEGIN;
select id from stakeholder where id = 5273 FOR UPDATE;

そして、同じ利害関係者に挿入されている他のレコードを実際にロックしました。するとLOCKが効いているようです。

しかし、同時アップロードでアプリケーションを実行すると、繰り返し表示されます。

誰かが私のトリガーの問題を見つけるのを手伝ってくれますか?

ありがとう、ダグラス。

4

1 に答える 1

1

あなたの考えは正しいです。別のフィールドに基づいて自動インクリメントを取得するには (グループに指定されているとしましょう)、シーケンスを使用することはできません。インクリメントする前に、そのグループの行をロックする必要があります。

トリガー関数のロジックがそれを行います。しかし、あなたは操作について誤解していPERFORMます。キーワードの代わりに配置されるはずなSELECTので、パラメーターとして文字列を受け取りません。これは、次のことを意味します。

PERFORM 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE';

PL/pgSQL は実際に実行されています。

SELECT 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE';

そして、結果を無視します。

この行でしなければならないことは次のとおりです。

PERFORM id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE;

それだけです。この行を変更するだけで完了です。

于 2013-11-07T12:18:45.293 に答える