こんにちは。エンジニアの藤原です。
最近はC#を用いたWebアプリケーション開発をしています。
データベースにはSQL Serverを採用していますが、ある日こんな問い合わせが・・。
一瞬「???」となった私の経験を備忘録として記事に残しておこうかと思います。
注意
本記事は基本的なデータベース概念を理解している方向けに記載しています。
目次
・何が起きたか
・原因
・解決策
何が起きたか
とある日、いつも通りPCに向かっていると他開発メンバーが開発した機能で問い合わせがきました。
「〇〇テーブルへの新規登録が全部コケるんですけど・・」
ステージング環境だったので良いですが、
開発者にとって唐突な問い合わせは本当に嫌なものですよね。
だけどそうもいっていられない。
まあ、見てみれば直ぐにわかるでしょ~と「承知です!確認します。」と返事しました。
(何故私にきた..)
機能としてはごく単純で、ボタンが押されたらテーブルAにINSERTするだけ。
いままでは普通に動いていたらしいし、、
取り合えずログを確認。
確かにInsertエラーログを出力していて、内容は「制約 '%.*ls' の %ls 違反。 オブジェクト '%.ls' には重複したキーを挿入できません。」初めての開発で親の顔ほどよく見るやつですね
これは簡単に解決できそうだ。
挿入対象のテーブル構造がどうだったかと定義書を確認していると、
あるカラムがPrimary KeyとしてIDENTITYプロパティを設定しており、INSERT時に自動的に一意の値を生成してくれています。
どうやらこの値が重複してINSERTが失敗している様子。
さらに情報が欲しかったのでヒアリングをすると、とあるメンバーが対象環境の該当テーブルに誤ってDELETEをかけてしまったとのこと。
原因
登録時の処理内容としては、最初の1レコード目を新規挿入時に必ずSEED値を0指定で挿入する作りになっており(それはそれでというのは一旦置いておいて)、最初の1レコード目の登録かどうかは登録レコード数で判定するフローになっていました。(元凶)
つまり、以下の流れで今回の事象が起きたと思われる。
1.何件かのレコードをテーブルAに登録
2.誤ってDELETE(IDENTITY の現在のカウントであるSEED値はそのまま)
3.登録レコードが0件であるため、SEED値を0指定で挿入を試みる
※DBCC CHECKIDENT('Users', RESEED, 0)
4.挿入失敗
それは当たり前に失敗しますね・・。
TRUNCATEならまだよかったですね。(良くない)
解決策
色々と作りに突っ込みどころは多いですが、かなり急ぎの対応でもあったので
IDENTITY をリセットする場合でも RESEED の値は MAX(ID) に合わせる修正
+フローの改修をすることで解決しました。
詳しく書けば、排他制御の考慮ももちろん欠かせないですが、
「IDENTITY便利だな~」だけの軽い考慮で使用するとこのようなことになるのでご注意を。
以上、藤原でした。