sql >> データベース >  >> RDS >> PostgreSQL

postgresqlでのROLLBACKイベントトリガー

    このためにシーケンスを使用することはできません。 すべてを通過する単一のシリアル化ポイントが必要です 挿入を行う必要があります。そうしないと、「ギャップレス」属性が保証されません。また、そのテーブルから行が削除されないようにする必要があります。

    シリアル化は、単一のトランザクションのみがそのテーブルに行を挿入できることも意味します。他のすべての挿入は、「前の」挿入がコミットまたはロールバックされるまで待機する必要があります。

    これを実装する方法の1つのパターンは、「シーケンス」番号が格納されているテーブルを作成することです。法的な理由でギャップレスでなければならない請求書番号にこれが必要であると仮定しましょう。

    したがって、最初に「現在の値」を保持するテーブルを作成します。

    create table slow_sequence 
    (
      seq_name        varchar(100) not null primary key,
      current_value   integer not null default 0
    );
    
    -- create a "sequence" for invoices
    insert into slow_sequence values ('invoice');
    

    次に、次の番号を生成する関数が必要ですが、2つのトランザクションが同時に次の番号を取得できないことを保証します。

    create or replace function next_number(p_seq_name text)
      returns integer
    as
    $$
      update slow_sequence
         set current_value = current_value + 1
      where seq_name = p_seq_name
      returning current_value;
    $$
    language sql;
    

    この関数はカウンターをインクリメントし、結果としてインクリメントされた値を返します。 updateのため これでシーケンスの行がロックされ、他のトランザクションはその値を更新できなくなります。呼び出し元のトランザクションがロールバックされると、シーケンスカウンターの更新もロールバックされます。コミットされると、新しい値が保持されます。

    すべてを確実にするため トランザクションは関数を使用します。トリガーを作成する必要があります。

    問題のテーブルを作成します:

    create table invoice 
    (
      invoice_number integer not null primary key, 
      customer_id    integer not null,
      due_date       date not null
    );
    

    次に、トリガー関数とトリガーを作成します。

    create or replace function f_invoice_trigger()
      returns trigger
    as
    $$
    begin
      -- the number is assigned unconditionally so that this can't 
      -- be prevented by supplying a specific number
      new.invoice_number := next_number('invoice');
      return new;
    end;
    $$
    language plpgsql;
    
    create trigger invoice_trigger
      before insert on invoice
      for each row
      execute procedure f_invoice_trigger();
    

    1つのトランザクションがこれを行う場合:

    insert into invoice (customer_id, due_date) 
    values (42, date '2015-12-01');
    

    新しい番号が生成されます。 次に、トランザクションは最初の挿入がコミットまたはロールバックされるまで待機する必要があります。

    私が言ったように、このソリューションはスケーラブルではありません。全くない。そのテーブルに多くの挿入がある場合、アプリケーションの速度が大幅に低下します。ただし、スケーラブルなの両方を使用することはできません。 ギャップレスシーケンスの正しい実装。

    また、上記のコードでカバーされていないエッジケースがあることもかなり確信しています。そのため、まだギャップが生じる可能性があります。




    1. SQL ServerでSAアカウントの名前を変更します(T-SQLの例)

    2. 2つのDjangoアプリ間でモデルを移動する方法(Django 1.7)

    3. アークウェアを選択する5つの理由

    4. postgresqlでパスワードをハッシュするにはどうすればよいですか?