バルクインサートの基本
こんにちは。
新卒で入社して2年目のエンジニアです。
今回は、実際の開発現場で遭遇したDBトラブルと、そこから学んだことを備忘録としてまとめます。
開発環境は以下の通りです。
- DB:Oracle Database
- 言語:C#
- ライブラリ:OracleDatabaseManager(Oracle.ManagedDataAccessを利用)
💡 OracleDBでよく使う操作3種
まず、私が現場でよく行っていたOracleDB操作を3つ挙げます。
- SELECT(データ取得)
- UPDATE / DELETE(データ更新・削除)
- INSERT(データ登録)
この中で、今回トラブルを起こしたのが INSERT(特にバルクインサート) です。
🚀 バルクインサートとは?
バルクインサート(Bulk Insert)は、
複数行のデータをまとめて登録することで、パフォーマンスを大きく向上させる方法です。
通常のINSERTを1件ずつ発行するよりも圧倒的に高速ですが、
一括処理ならではのリスクもあります。
C#では、以下のようなコードで実装していました。
using (var connection = new OracleConnection(connectionString))
{
connection.Open();
using (var bulkCopy = new OracleBulkCopy(connection))
{
bulkCopy.DestinationTableName = "MY_TABLE";
bulkCopy.WriteToServer(dataTable);
}
}
💥 トラブル発生:インデックス破損
ある日、開発DBでバルクインサートを実行していたところ、
途中でアプリが強制終了してしまいました。
その後、再度クエリを実行すると異常が…。
「一意制約違反(ORA-00001)」
「インデックス破損の可能性」
調査の結果、テーブルのインデックスが破壊されていたことが判明しました。
バルクインサート中にプロセスが中断されたため、
一意制約が途中で機能せず、重複データが生成されてしまったのが原因でした。
🔧 修復方法
幸い、開発環境だったため上長に報告し、
以下のような手順で修復を行いました。
1. 問題のテーブルを特定
SELECT index_name, status FROM user_indexes WHERE table_name = 'MY_TABLE';
2. インデックスの再構築
ALTER INDEX IDX_MY_TABLE REBUILD;
3. 状態確認
SELECT index_name, status FROM user_indexes WHERE table_name = 'MY_TABLE';
これで、インデックスの状態が VALID に戻り、正常に動作するようになりました。
とはいえ、開発環境とはいえ冷や汗ものでした…。
🧠 今回の学びと対策
今回の経験から、次のような対策を取ることにしました。
✅ 対策1:トランザクション管理の徹底
バルクインサートを行う場合は必ず
BEGIN TRANSACTION ~ COMMIT を明示的に制御し、
途中停止時に ROLLBACK が確実に走るようにします。
✅ 対策2:一意制約の再確認
大量データを挿入する際は、あらかじめ一意制約の対象カラムを確認し、
重複チェックをコード側で行うようにしました。
✅ 対策3:バルク処理のテスト環境分離
検証段階では専用の検証用DBを用意し、
本番データや重要インデックスに影響しない環境でテストを行うようにしました。
✏️ まとめ
バルクインサートは高速だが、一括処理によるリスクも大きい
途中停止や例外処理時の整合性に注意
インデックス破損は、REBUILD で回復できるが、そもそも起こさないことが重要
開発現場では、「ただ動く」よりも
「安全に動く」ことが何より大切だと痛感しました。
🙌 最後に
今回の失敗はヒヤッとしましたが、
トラブルを通してDBの仕組みや整合性の大切さをより深く理解できたと感じています。
同じようにOracleDBでバルク処理を使う方の参考になれば幸いです。