SQLiteを使っていて、「テーブルの中身を全部削除したい!」という場面は意外と多いですよね。
たとえば、テストデータをリセットしたいとき、古いデータを一括で消したいときなど。
よくある削除の場面
- テストデータのリセット
- 開発環境の初期化
- ログデータの定期的なクリア
- データの移行前の準備
- 不要になった古いデータの削除
この記事では、SQLiteで全レコードを削除する方法を、注意点とともにわかりやすく解説します。
SQLiteとは(基本知識)
まず、SQLiteについて簡単に説明します。
SQLiteの特徴
- ファイルベースの軽量なデータベース
- インストール不要で使える
- アプリケーションに組み込みやすい
- 多くのプログラミング言語でサポート
レコードとは テーブルに保存されている1行分のデータのことです。例えば、ユーザー情報テーブルの場合、1人分の情報が1レコードになります。
DELETE文で全レコード削除
基本構文
最も基本的な全レコード削除の方法は、DELETE文を使うことです。
DELETE FROM テーブル名;
具体例
DELETE FROM users;
このSQL文は、users
テーブルのすべての行データを削除します。ただし、テーブル構造(列や型)は残ります。
削除前の確認方法
削除する前に、どのくらいのデータがあるか確認しましょう:
-- レコード数を確認
SELECT COUNT(*) FROM users;
-- データの内容を確認(最初の10件)
SELECT * FROM users LIMIT 10;
-- テーブル構造を確認
.schema users
実行例(詳細)
-- 削除前の状態確認
SELECT COUNT(*) AS 'レコード数' FROM users;
-- 結果例: レコード数 = 1000
-- 全レコード削除実行
DELETE FROM users;
-- 削除後の確認
SELECT COUNT(*) AS 'レコード数' FROM users;
-- 結果: レコード数 = 0
-- テーブル構造は残っていることを確認
.schema users
-- 結果: CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);
WHERE句を使った削除との違い
-- 特定条件での削除
DELETE FROM users WHERE age < 18;
-- 全削除(WHERE句なし)
DELETE FROM users;
-- 全削除(WHERE句で常に真の条件)
DELETE FROM users WHERE 1=1;
DELETE FROM users WHERE TRUE;
最後の2つの書き方も全削除と同じ効果がありますが、誤解を招きやすいため推奨しません。
SQLiteにはTRUNCATEがない!
多くのデータベース(MySQL、PostgreSQLなど)では、TRUNCATE TABLE
という高速削除コマンドがありますが、SQLiteにはこの構文が存在しません。
他のデータベースでのTRUNCATE
-- MySQL、PostgreSQLなどでは使用可能(SQLiteでは使えない)
TRUNCATE TABLE users;
SQLiteでTRUNCATEと同等の効果を得る方法
SQLiteでTRUNCATEと同じような効果を得るには、以下の方法があります:
方法1:DELETE + VACUUM
DELETE FROM users;
VACUUM;
方法2:テーブルの再作成
DROP TABLE users;
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
);
方法3:DELETE + AUTO INCREMENT初期化
DELETE FROM users;
DELETE FROM sqlite_sequence WHERE name='users';
自動採番(AUTOINCREMENT)の扱い
SQLiteのAUTOINCREMENT機能について詳しく説明します。
AUTOINCREMENTとは
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
email TEXT
);
このテーブルでは、id
列が自動的に1, 2, 3…と連番になります。
DELETEでは自動採番がリセットされない
-- データ挿入
INSERT INTO users (name, email) VALUES ('田中', 'tanaka@example.com');
INSERT INTO users (name, email) VALUES ('佐藤', 'sato@example.com');
-- この時点で id は 1, 2
-- 全削除
DELETE FROM users;
-- 新しいデータ挿入
INSERT INTO users (name, email) VALUES ('鈴木', 'suzuki@example.com');
-- id は 3 になる(1や2ではない)
AUTOINCREMENTをリセットする方法
-- 方法1: sqlite_sequenceテーブルを直接操作
DELETE FROM users;
DELETE FROM sqlite_sequence WHERE name='users';
-- 方法2: VACUUMと組み合わせ
DELETE FROM users;
VACUUM;
-- 注意: VACUUMだけではAUTOINCREMENTはリセットされません
-- 方法3: テーブル再作成
DROP TABLE users;
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
email TEXT
);
sqlite_sequenceテーブルの確認
-- 現在のAUTOINCREMENT値を確認
SELECT * FROM sqlite_sequence WHERE name='users';
-- 手動でリセット
UPDATE sqlite_sequence SET seq = 0 WHERE name='users';
VACUUM文による最適化
VACUUMとは
VACUUMは、SQLiteデータベースファイルの最適化を行うコマンドです。
VACUUMの効果
- 削除されたデータの領域を回収
- ファイルサイズの縮小
- 断片化の解消
- パフォーマンスの改善
VACUUMの実行方法
-- 基本的な実行
VACUUM;
-- 特定のスキーマに対して実行
VACUUM main;
-- 出力先を指定してVACUUM
VACUUM INTO 'backup.db';
DELETE + VACUUMの効果確認
-- ファイルサイズ確認(コマンドライン)
.shell ls -lh database.db
-- データベース情報確認
PRAGMA database_list;
-- ページ情報確認
PRAGMA page_count;
PRAGMA page_size;
実用例
-- 大量データの削除とファイル最適化
DELETE FROM large_table;
VACUUM;
-- 結果: ファイルサイズが大幅に縮小される
複数の削除方法の比較
方法別の特徴まとめ
方法 | 実行速度 | ID初期化 | 構造保持 | ファイルサイズ | 用途 |
---|---|---|---|---|---|
DELETE FROM | 普通 | × | ○ | 変わらず | 基本的な削除 |
DELETE + VACUUM | 遅い | × | ○ | 縮小 | 容量節約重視 |
DELETE + sqlite_sequence削除 | 普通 | ○ | ○ | 変わらず | ID初期化重視 |
DROP + CREATE | 速い | ○ | △ | 大幅縮小 | 完全リセット |
パフォーマンステスト例
-- テスト用大量データ作成
CREATE TABLE test_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT
);
-- 100万件のデータ挿入
INSERT INTO test_table (data)
SELECT 'test_data_' || (ROW_NUMBER() OVER())
FROM (SELECT 0 UNION ALL SELECT 1) t1,
(SELECT 0 UNION ALL SELECT 1) t2,
-- ... 20回繰り返し
LIMIT 1000000;
-- 削除時間の測定
.timer ON
-- 方法1: 単純DELETE
DELETE FROM test_table;
-- 方法2: DELETE + VACUUM
DELETE FROM test_table;
VACUUM;
.timer OFF
Pythonでの実装例
基本的な削除スクリプト
import sqlite3
import os
def delete_all_records(db_path, table_name):
"""指定テーブルの全レコードを削除"""
try:
# データベース接続
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 削除前のレコード数確認
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
before_count = cursor.fetchone()[0]
print(f"削除前のレコード数: {before_count}")
# 全レコード削除
cursor.execute(f"DELETE FROM {table_name}")
# 削除後のレコード数確認
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
after_count = cursor.fetchone()[0]
print(f"削除後のレコード数: {after_count}")
# 変更をコミット
conn.commit()
print(f"{before_count}件のレコードを削除しました")
except sqlite3.Error as e:
print(f"エラーが発生しました: {e}")
conn.rollback()
finally:
if conn:
conn.close()
# 使用例
delete_all_records('example.db', 'users')
より高度な削除スクリプト
import sqlite3
import os
from datetime import datetime
class SQLiteManager:
def __init__(self, db_path):
self.db_path = db_path
def connect(self):
"""データベースに接続"""
return sqlite3.connect(self.db_path)
def backup_database(self):
"""削除前にバックアップを作成"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.db_path}.backup_{timestamp}"
try:
with self.connect() as conn:
with open(backup_path, 'wb') as backup_file:
for line in conn.iterdump():
backup_file.write(f"{line}\n".encode())
print(f"バックアップを作成しました: {backup_path}")
return backup_path
except Exception as e:
print(f"バックアップエラー: {e}")
return None
def delete_all_with_options(self, table_name, reset_autoincrement=False,
vacuum=False, backup=True):
"""オプション付き全削除"""
if backup:
backup_path = self.backup_database()
if not backup_path:
print("バックアップに失敗したため、削除を中止します")
return False
try:
with self.connect() as conn:
cursor = conn.cursor()
# 削除前の情報取得
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
record_count = cursor.fetchone()[0]
cursor.execute("SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size()")
file_size_before = cursor.fetchone()[0]
print(f"削除前: レコード数={record_count}, ファイルサイズ={file_size_before:,}bytes")
# 全レコード削除
cursor.execute(f"DELETE FROM {table_name}")
# AUTOINCREMENTリセット(オプション)
if reset_autoincrement:
cursor.execute(f"DELETE FROM sqlite_sequence WHERE name='{table_name}'")
print("AUTOINCREMENTをリセットしました")
# VACUUM実行(オプション)
if vacuum:
cursor.execute("VACUUM")
print("VACUUMを実行しました")
# 削除後の情報取得
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
record_count_after = cursor.fetchone()[0]
cursor.execute("SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size()")
file_size_after = cursor.fetchone()[0]
print(f"削除後: レコード数={record_count_after}, ファイルサイズ={file_size_after:,}bytes")
print(f"ファイルサイズ削減: {file_size_before - file_size_after:,}bytes")
return True
except sqlite3.Error as e:
print(f"削除エラー: {e}")
return False
# 使用例
manager = SQLiteManager('example.db')
# 基本的な削除
manager.delete_all_with_options('users')
# 全オプション有効
manager.delete_all_with_options('logs',
reset_autoincrement=True,
vacuum=True,
backup=True)
エラーハンドリングを含む安全な削除
import sqlite3
import sys
def safe_delete_all(db_path, table_name, confirm=True):
"""安全な全削除(確認付き)"""
# 確認メッセージ
if confirm:
print(f"警告: {table_name}テーブルの全データを削除します")
response = input("本当に削除しますか? (yes/no): ")
if response.lower() != 'yes':
print("削除をキャンセルしました")
return False
try:
# ファイル存在確認
if not os.path.exists(db_path):
print(f"データベースファイルが見つかりません: {db_path}")
return False
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# テーブル存在確認
cursor.execute("""
SELECT name FROM sqlite_master
WHERE type='table' AND name=?
""", (table_name,))
if not cursor.fetchone():
print(f"テーブルが見つかりません: {table_name}")
return False
# トランザション開始
cursor.execute("BEGIN TRANSACTION")
try:
# 削除実行
cursor.execute(f"DELETE FROM {table_name}")
deleted_count = cursor.rowcount
# コミット
cursor.execute("COMMIT")
print(f"正常に削除されました: {deleted_count}件")
return True
except Exception as e:
# ロールバック
cursor.execute("ROLLBACK")
print(f"削除中にエラーが発生しました: {e}")
return False
except sqlite3.Error as e:
print(f"データベースエラー: {e}")
return False
# 使用例
if __name__ == "__main__":
safe_delete_all('example.db', 'users', confirm=True)
削除の注意点とベストプラクティス
重要な注意点
1. バックアップの重要性
# 削除前に必ずバックアップ
import shutil
shutil.copy2('original.db', 'backup.db')
2. トランザクションの使用
BEGIN TRANSACTION;
DELETE FROM users;
-- 問題なければ
COMMIT;
-- 問題があれば
-- ROLLBACK;
3. 外部キー制約の確認
-- 外部キー制約を確認
PRAGMA foreign_key_list(users);
-- 外部キー制約を一時的に無効化
PRAGMA foreign_keys = OFF;
DELETE FROM users;
PRAGMA foreign_keys = ON;
パフォーマンス最適化
1. 大量データの削除
-- 一度に削除せず、分割して削除
DELETE FROM large_table WHERE id IN (
SELECT id FROM large_table LIMIT 10000
);
-- これを繰り返し実行
2. インデックスの考慮
-- 削除前にインデックスを削除
DROP INDEX IF EXISTS idx_users_email;
-- データ削除
DELETE FROM users;
-- インデックスを再作成
CREATE INDEX idx_users_email ON users(email);
削除の確認方法
-- 削除前の状態確認
.tables
.schema users
SELECT COUNT(*) FROM users;
-- 削除実行
DELETE FROM users;
-- 削除後の確認
SELECT COUNT(*) FROM users;
PRAGMA table_info(users); -- 構造が残っていることを確認
応用例とよくある使用場面
テストデータのリセット
def reset_test_data():
"""テスト用データベースの初期化"""
tables_to_clear = ['users', 'posts', 'comments', 'logs']
with sqlite3.connect('test.db') as conn:
cursor = conn.cursor()
# 外部キー制約を無効化
cursor.execute("PRAGMA foreign_keys = OFF")
for table in tables_to_clear:
try:
cursor.execute(f"DELETE FROM {table}")
cursor.execute(f"DELETE FROM sqlite_sequence WHERE name='{table}'")
print(f"{table} テーブルをクリアしました")
except sqlite3.Error as e:
print(f"{table} のクリアに失敗: {e}")
# 外部キー制約を再有効化
cursor.execute("PRAGMA foreign_keys = ON")
print("テストデータのリセットが完了しました")
# 使用例
reset_test_data()
ログデータの定期削除
import sqlite3
from datetime import datetime, timedelta
def cleanup_old_logs(days_to_keep=30):
"""指定日数より古いログを削除"""
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
with sqlite3.connect('app.db') as conn:
cursor = conn.cursor()
# 削除対象の確認
cursor.execute("""
SELECT COUNT(*) FROM logs
WHERE created_at < ?
""", (cutoff_date.isoformat(),))
count_to_delete = cursor.fetchone()[0]
if count_to_delete == 0:
print("削除対象のログはありません")
return
print(f"{count_to_delete}件の古いログを削除します")
# 古いログを削除
cursor.execute("""
DELETE FROM logs
WHERE created_at < ?
""", (cutoff_date.isoformat(),))
print(f"{cursor.rowcount}件のログを削除しました")
# ファイルサイズ最適化
cursor.execute("VACUUM")
print("データベースを最適化しました")
# 使用例
cleanup_old_logs(30) # 30日より古いログを削除
開発環境の初期化スクリプト
import sqlite3
import os
def initialize_dev_environment():
"""開発環境の初期化"""
db_path = 'development.db'
# バックアップ作成
if os.path.exists(db_path):
backup_path = f"{db_path}.backup"
shutil.copy2(db_path, backup_path)
print(f"バックアップを作成: {backup_path}")
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# 全てのユーザーデータテーブルをクリア
user_tables = ['users', 'profiles', 'posts', 'comments', 'likes']
for table in user_tables:
cursor.execute(f"DELETE FROM {table}")
cursor.execute(f"DELETE FROM sqlite_sequence WHERE name='{table}'")
# 初期管理者アカウントを作成
cursor.execute("""
INSERT INTO users (username, email, is_admin)
VALUES ('admin', 'admin@example.com', 1)
""")
# テストデータを少量挿入
test_users = [
('test1', 'test1@example.com', 0),
('test2', 'test2@example.com', 0),
]
cursor.executemany("""
INSERT INTO users (username, email, is_admin)
VALUES (?, ?, ?)
""", test_users)
print("開発環境の初期化が完了しました")
print(f"管理者アカウント: admin")
print(f"テストアカウント: test1, test2")
# 使用例
initialize_dev_environment()
まとめ
SQLiteでの全削除は簡単ですが、取り返しのつかない操作でもあるため、実行前には必ずバックアップを取りましょう。
重要なポイント
- 基本的な削除:
DELETE FROM テーブル名;
- SQLiteにはTRUNCATE文がない
- AUTOINCREMENTは自動でリセットされない
- VACUUMでファイルサイズを最適化
- 必ずバックアップを取る
方法別の選択指針
- 基本的な削除:
DELETE FROM テーブル名
- ID初期化が必要:
DELETE + sqlite_sequence削除
- 容量節約が必要:
DELETE + VACUUM
- 完全リセット:
DROP + CREATE
コメント