簡単に解決できる可能性のあるシナリオがいくつかありますが、そうではない厄介なシナリオがあります。
値を入力するユーザーの場合、INSERTが問題を検出する前に、しばらくしてから同じ値を入力します。これは、あるユーザーが値を送信し、しばらくしてから別のユーザーが同じ値を送信する場合に機能します。
ユーザーが重複のある値のリストを送信する場合-たとえば、{ABC、DEF、ABC}-コードの1回の呼び出しで、アプリケーションは重複を検出してフィルタリングでき、おそらくエラーがスローされます。挿入する前に、DBに一意の値が含まれていないことも確認する必要があります。
トリッキーなシナリオは、あるユーザーの書き込みが別のユーザーの書き込みと同時にDBMS内にあり、同じ値を書き込んでいる場合です。その後、あなたはそれらの間の競争条件を持っています。DBMSは(ほとんどの場合-どちらを使用しているかはわかりません)、プリエンプティブマルチタスクシステムであるため、実行中のどの時点でもタスクを一時停止できます。つまり、user1のタスクは既存の行がないことを確認でき、user2のタスクは既存の行がないことを確認でき、user1のタスクはその行を挿入でき、user2のタスクはその行を挿入できます。各時点で、タスクはそれぞれが正しいことを行って満足しています。ただし、全体的にエラーが発生します。
通常、DBMSは問題の値にロックをかけることでこれを処理します。この問題では、新しい行を作成しているため、ロックするものはまだありません。答えはレンジロックです。これにより、現在存在するかどうかに関係なく、値の範囲がロックされます。ロックされると、その範囲は、ロックが解除されるまで別のタスクからアクセスできません。範囲ロックを取得するには、SERIALIZABLEの分離レベルを指定する必要があります。タスクがチェックした後に別のタスクが続けてこっそりと移動する現象は、ファントムレコードと呼ばれます。
アプリケーション全体で分離レベルをSerializableに設定すると、影響があります。スループットが低下します。過去に十分に機能していた他の競合状態は、エラーを表示し始める可能性があります。重複を引き起こすコードを実行する接続に設定し、アプリケーションの残りの部分はそのままにしておくことをお勧めします。
コードベースの代替方法は、書き込み前ではなく書き込み後にチェックすることです。INSERTを実行し、そのハッシュ値を持つ行の数をカウントします。重複がある場合は、アクションをロールバックします。これは、いくつかの不可解な結果をもたらす可能性があります。タスク1がタスク2を書き込み、次にタスク1が重複をチェックして見つけたとします。最初でもロールバックします。同様に、両方のタスクが重複と両方のロールバックを検出する場合があります。しかし、少なくとも、操作するメッセージ、再試行メカニズムがあり、新しい重複はありません。プログラムフローを制御するために例外を使用するのと同じように、ロールバックは眉をひそめています。注同様にその全てトランザクションの作業は、複製を引き起こす書き込みだけでなく、ロールバックされます。また、同時実行性を低下させる可能性のある明示的なトランザクションが必要になります。ハッシュにインデックスがない限り、重複チェックはひどく遅くなります。あなたがするなら、あなたはそれをユニークなものにすることもできます!
あなたがコメントしたように、本当の解決策はユニークなインデックスです。これはあなたのメンテナンスウィンドウに収まるはずです(もちろんあなたはあなたのシステムを最もよく知っていますが)。ハッシュが8バイトであるとしましょう。1億行の場合、約1GBです。経験上、妥当なビットのハードウェアがこれらの多くの行を1〜2分で処理することが示唆されています。重複したチェックと削除はこれに追加されますが、事前にスクリプト化することができます。ただし、これは余談です。