Bulk INSERTをトランザクションでラップする
BEGIN/COMMITトランザクションブロックでラップされたBulk INSERT文を生成し、原子性とパフォーマンスの向上を実現します。
Advanced
詳細な説明
トランザクションでラップされたBulkインサート
Bulk INSERT文をトランザクションでラップすると2つのメリットがあります:原子性(全成功か全失敗か)とパフォーマンス(コミットオーバーヘッドの削減)。
基本的なトランザクションラッパー
BEGIN;
INSERT INTO "events" ("id", "type", "timestamp")
VALUES
(1, 'login', '2024-01-01 09:00:00'),
(2, 'click', '2024-01-01 09:01:00'),
-- ... さらに行 ...
(100, 'logout', '2024-01-01 10:00:00');
INSERT INTO "events" ("id", "type", "timestamp")
VALUES
(101, 'login', '2024-01-01 11:00:00'),
-- ... 次のバッチ ...
(200, 'purchase', '2024-01-01 12:00:00');
COMMIT;
SQL Serverバリアント
SQL ServerはBEGINの代わりにBEGIN TRANSACTIONを使用します:
BEGIN TRANSACTION;
INSERT INTO [events] ([id], [type], [timestamp])
VALUES
(1, 'login', '2024-01-01 09:00:00');
COMMIT;
トランザクションが重要な理由
トランザクションなし: 各INSERT文が個別に自動コミットされます。
- バッチ10のうちバッチ5が失敗した場合、バッチ1-4は既にコミット済み
- 各自動コミットがディスク同期をトリガー(HDDでは低速)
トランザクションあり: すべてのバッチが成功するか、すべてがロールバックされます。
- 原子的:部分的なインポートは不可能
- 高速:N回の同期ではなくCOMMIT時に1回のディスク同期
パフォーマンスへの影響
| シナリオ | SQLite(1000行) | PostgreSQL(1000行) |
|---|---|---|
| トランザクションなし | 約2秒 | 約500ms |
| トランザクションあり | 約50ms | 約100ms |
速度向上はSQLiteで最も劇的で、各自動コミットがファイルシステム同期を強制します。
ベストプラクティス
- トランザクションは短く保つ(10,000行未満)— ロック競合を回避
- スクリプトにエラーハンドリングを追加:
BEGIN; INSERT ...; INSERT ...; COMMIT;で失敗時にロールバック - 非常に大規模なインポートでは、5,000-10,000行ごとに複数のトランザクションに分割
ユースケース
テスト環境用のデータベースシードスクリプトを作成している場合。すべてのINSERTを単一のトランザクションでラップすることで、すべてのテストデータがロードされるか、まったくロードされないかのいずれかを保証し、部分的にシードされたデータベースによるテスト失敗を防ぎます。