シーケンスはギャップのない数値のセットを生成しません。ロールバックまたはエラーによってシーケンス番号が「使用」されるため、実際にはギャップのない数値のセットを生成する方法はありません。
少し前にこれに関する記事を書きました。これはOracleを対象としていますが、実際にはギャップのない数値の基本原則に関するものであり、ここでも同じことが当てはまると思います。
まあ、それはまた起こった。誰かがギャップのない一連の数字を生成するための要件を実装する方法を尋ねました、そしてこれがシステムパフォーマンスを殺すだろうと言うために否定的な発言者の群れが彼らに降りてきました(そしてここで私は少し言い換えます)、それはめったに有効な要件ではありません、要件を書いた人は誰でもばかげた何とか何とか何とかです。
スレッドで指摘しているように、ギャップのない一連の数値を生成することが、真の法的要件である場合があります。 VAT(消費税)が登録されている英国の2,000,000以上の組織の請求書番号にはそのような要件があり、その理由はかなり明白です。税務当局からの収入の生成を隠すことがより困難になるためです。スペインとポルトガルでは要件であるというコメントを見てきましたが、他の多くの国では要件でなくても驚かないでしょう。
それで、それが有効な要件であると認める場合、どのような状況でギャップのない一連の数*が問題になるのでしょうか。集団思考は、それが常にそうであるとあなたに信じさせることがよくありますが、実際には、それは非常に特定の状況下での潜在的な問題にすぎません。
- 一連の数字にギャップがあってはなりません。
- 複数のプロセスにより、番号が関連付けられているエンティティ(請求書など)が作成されます。
- 番号は、エンティティの作成時に生成する必要があります。
これらの要件をすべて満たす必要がある場合は、アプリケーションにシリアル化のポイントがあります。これについては後で説明します。
最初に、一連の数字の要件のいずれかを削除できる場合に、それらの要件を実装する方法について説明しましょう。
一連の数値にギャップがある可能性がある場合(および数値の即時生成を必要とする複数のプロセスがある場合)、OracleSequenceオブジェクトを使用します。それらは非常に高性能であり、ギャップが予想される状況は非常によく議論されています。重要な場合は、番号の生成からトランザクションのコミットまでの間にプロセス障害が発生する可能性を最小限に抑えるように設計作業を行うことで、スキップされる番号の量を最小限に抑えることはそれほど難しくありません。
請求書のバッチ生成の場合のように、エンティティを作成する複数のプロセスがない場合(そして、即座に生成する必要があるギャップのない一連の番号が必要な場合)、すでにシリアル化のポイントがあります。それ自体は問題ではない可能性があり、必要な操作を実行するための効率的な方法である可能性があります。この場合、ギャップのない数値を生成するのは簡単です。現在の最大値を読み取り、さまざまな手法ですべてのエンティティに増分値を適用できます。たとえば、請求書の新しいバッチを一時的な作業テーブルから請求書テーブルに挿入する場合は、次のようになります。
insert into
invoices
(
invoice#,
...)
with curr as (
select Coalesce(Max(invoice#)) max_invoice#
from invoices)
select
curr.max_invoice#+rownum,
...
from
tmp_invoice
...
もちろん、一度に1つのインスタンスのみを実行できるようにプロセスを保護し(おそらくOracleを使用している場合はDBMS_Lockを使用)、一意のキーとは対照的に請求書番号を保護します。あなたは本当に、本当に気にしています。
番号の即時生成が必要ない場合(ただし、ギャップがなく、複数のプロセスがエンティティを生成する必要がある場合)、エンティティの生成とトランザクションのコミットを許可してから、番号の生成を単一のバッチに任せることができます。仕事。エンティティテーブルの更新、または別のテーブルへの挿入。
では、複数のプロセスによってギャップのない一連の数値を瞬時に生成するという3つの要素が必要な場合はどうでしょうか。私たちにできることは、プロセスのシリアル化の期間を最小限に抑えることです。次のアドバイスを提供し、追加のアドバイス(またはもちろん反対のアドバイス)を歓迎します。
- 現在の値を専用のテーブルに保存します。シーケンスは使用しないでください。
- 関数またはプロシージャにカプセル化することにより、すべてのプロセスが同じコードを使用して新しい数値を生成するようにします。
- DBMS_Lockを使用して番号ジェネレーターへのアクセスをシリアル化し、各シリーズに専用のロックがあることを確認します。
- コミット時にロックを解除してエンティティ作成トランザクションが完了するまで、シリーズジェネレーターでロックを保持します
- 番号の生成を可能な限り最後の瞬間まで遅らせます。
- 番号を生成した後、コミットが完了する前に、予期しないエラーの影響を考慮してください。アプリケーションは正常にロールバックしてロックを解放しますか、それともセッションが後で切断されるまでシリーズジェネレーターのロックを保持しますか?どの方法を使用する場合でも、トランザクションが失敗した場合は、シリーズ番号を「プールに戻す」必要があります。
- エンティティのテーブルのトリガーにすべてをカプセル化できますか?行を挿入して挿入を自動的にコミットするテーブルまたは他のAPI呼び出しにカプセル化できますか?
元の記事