-
-
Save kobitoDevelopment/e22b580322bff1443a6cfa0f48088354 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * データベース削除の4つのアプローチ | |
| * MySQL + PHP サンプルコード | |
| */ | |
| // ======================================== | |
| // 各アプローチの比較と使い分け | |
| // ======================================== | |
| /** | |
| * 1. 論理削除(deleted_at) | |
| * メリット: 復元が容易、削除履歴が残る、誤削除に強い、削除日時が分かる | |
| * デメリット: データ量が増える、クエリが複雑になる | |
| * 用途: ユーザーアカウント、重要なマスターデータ、監査証跡が必要な場合 | |
| * | |
| * 2. 物理削除 | |
| * メリット: データベースがシンプル、ストレージ節約、完全にデータを消去できる | |
| * デメリット: 復元不可、履歴が残らない | |
| * 用途: 一時データ、セッション情報、キャッシュ、個人情報の完全削除 | |
| * | |
| * 3. 削除フラグ(is_deleted) | |
| * メリット: シンプル、高速、復元が容易、インデックスが効きやすい | |
| * デメリット: deleted_atより情報が少ない(削除日時が分からない) | |
| * 用途: コンテンツ管理、注文管理、シンプルな論理削除が必要な場合 | |
| * | |
| * 4. 削除テーブルへの移動 | |
| * メリット: 本テーブルのパフォーマンス維持、監査証跡の保持、削除者の記録も可能 | |
| * デメリット: 実装が複雑、トランザクション管理が必要 | |
| * 用途: 大量データの削除、データを完全に削除できず保持しておかなければならない | |
| * 責務が要件に含まれる場合、コンプライアンス要件がある場合 | |
| */ | |
| // データベース接続設定 | |
| $host = 'localhost'; | |
| $dbname = 'my_dbname'; | |
| $username = 'your_username'; | |
| $password = 'your_password'; | |
| try { | |
| $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password); | |
| $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
| } catch (PDOException $e) { | |
| die("接続エラー: " . $e->getMessage()); | |
| } | |
| // ======================================== | |
| // 1. 論理削除(deleted_atカラムを使用) | |
| // ======================================== | |
| /** | |
| * テーブル作成例(論理削除用) | |
| * CREATE TABLE users ( | |
| * id INT PRIMARY KEY AUTO_INCREMENT, | |
| * name VARCHAR(100), | |
| * email VARCHAR(100), | |
| * created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| * deleted_at TIMESTAMP NULL DEFAULT NULL | |
| * ); | |
| */ | |
| function logicalDelete($pdo, $userId) { | |
| $sql = "UPDATE users SET deleted_at = NOW() WHERE id = :id AND deleted_at IS NULL"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $userId]); | |
| return $stmt->rowCount() > 0; | |
| } | |
| function getActiveUsers($pdo) { | |
| $sql = "SELECT * FROM users WHERE deleted_at IS NULL"; | |
| $stmt = $pdo->query($sql); | |
| return $stmt->fetchAll(PDO::FETCH_ASSOC); | |
| } | |
| function restoreUser($pdo, $userId) { | |
| $sql = "UPDATE users SET deleted_at = NULL WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $userId]); | |
| return $stmt->rowCount() > 0; | |
| } | |
| // 使用例 | |
| // logicalDelete($pdo, 1); | |
| // $activeUsers = getActiveUsers($pdo); | |
| // ======================================== | |
| // 2. 物理削除(完全にデータを削除) | |
| // ======================================== | |
| /** | |
| * テーブル作成例(物理削除用) | |
| * CREATE TABLE products ( | |
| * id INT PRIMARY KEY AUTO_INCREMENT, | |
| * name VARCHAR(100), | |
| * price DECIMAL(10, 2), | |
| * created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| * ); | |
| */ | |
| function physicalDelete($pdo, $productId) { | |
| $sql = "DELETE FROM products WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $productId]); | |
| return $stmt->rowCount() > 0; | |
| } | |
| function physicalDeleteWithCondition($pdo, $condition) { | |
| // 例: 1年以上前のデータを削除 | |
| $sql = "DELETE FROM products WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR)"; | |
| $stmt = $pdo->query($sql); | |
| return $stmt->rowCount(); | |
| } | |
| // 使用例 | |
| // physicalDelete($pdo, 5); | |
| // ======================================== | |
| // 3. 削除フラグ(is_deletedカラムを使用) | |
| // ======================================== | |
| /** | |
| * テーブル作成例(削除フラグ用) | |
| * CREATE TABLE orders ( | |
| * id INT PRIMARY KEY AUTO_INCREMENT, | |
| * customer_name VARCHAR(100), | |
| * total_amount DECIMAL(10, 2), | |
| * is_deleted TINYINT(1) DEFAULT 0, | |
| * created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| * updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | |
| * ); | |
| */ | |
| function deleteFlagDelete($pdo, $orderId) { | |
| $sql = "UPDATE orders SET is_deleted = 1 WHERE id = :id AND is_deleted = 0"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $orderId]); | |
| return $stmt->rowCount() > 0; | |
| } | |
| function getActiveOrders($pdo) { | |
| $sql = "SELECT * FROM orders WHERE is_deleted = 0"; | |
| $stmt = $pdo->query($sql); | |
| return $stmt->fetchAll(PDO::FETCH_ASSOC); | |
| } | |
| function undeleteOrder($pdo, $orderId) { | |
| $sql = "UPDATE orders SET is_deleted = 0 WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $orderId]); | |
| return $stmt->rowCount() > 0; | |
| } | |
| // 使用例 | |
| // deleteFlagDelete($pdo, 10); | |
| // $orders = getActiveOrders($pdo); | |
| // ======================================== | |
| // 4. 削除テーブルへの移動 | |
| // ======================================== | |
| /** | |
| * テーブル作成例(削除テーブルへの移動用) | |
| * CREATE TABLE posts ( | |
| * id INT PRIMARY KEY AUTO_INCREMENT, | |
| * title VARCHAR(200), | |
| * content TEXT, | |
| * author_id INT, | |
| * created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| * ); | |
| * | |
| * CREATE TABLE deleted_posts ( | |
| * id INT PRIMARY KEY, | |
| * title VARCHAR(200), | |
| * content TEXT, | |
| * author_id INT, | |
| * created_at TIMESTAMP, | |
| * deleted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| * deleted_by INT | |
| * ); | |
| */ | |
| function moveToDeletedTable($pdo, $postId, $deletedBy) { | |
| try { | |
| $pdo->beginTransaction(); | |
| // 元のデータを取得 | |
| $sql = "SELECT * FROM posts WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $postId]); | |
| $post = $stmt->fetch(PDO::FETCH_ASSOC); | |
| if (!$post) { | |
| throw new Exception("投稿が見つかりません"); | |
| } | |
| // 削除テーブルに挿入 | |
| $sql = "INSERT INTO deleted_posts (id, title, content, author_id, created_at, deleted_by) | |
| VALUES (:id, :title, :content, :author_id, :created_at, :deleted_by)"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute([ | |
| 'id' => $post['id'], | |
| 'title' => $post['title'], | |
| 'content' => $post['content'], | |
| 'author_id' => $post['author_id'], | |
| 'created_at' => $post['created_at'], | |
| 'deleted_by' => $deletedBy | |
| ]); | |
| // 元のテーブルから削除 | |
| $sql = "DELETE FROM posts WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $postId]); | |
| $pdo->commit(); | |
| return true; | |
| } catch (Exception $e) { | |
| $pdo->rollBack(); | |
| error_log("削除エラー: " . $e->getMessage()); | |
| return false; | |
| } | |
| } | |
| function restoreFromDeletedTable($pdo, $postId) { | |
| try { | |
| $pdo->beginTransaction(); | |
| // 削除テーブルからデータを取得 | |
| $sql = "SELECT id, title, content, author_id, created_at FROM deleted_posts WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $postId]); | |
| $post = $stmt->fetch(PDO::FETCH_ASSOC); | |
| if (!$post) { | |
| throw new Exception("削除された投稿が見つかりません"); | |
| } | |
| // 元のテーブルに復元 | |
| $sql = "INSERT INTO posts (id, title, content, author_id, created_at) | |
| VALUES (:id, :title, :content, :author_id, :created_at)"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute($post); | |
| // 削除テーブルから削除 | |
| $sql = "DELETE FROM deleted_posts WHERE id = :id"; | |
| $stmt = $pdo->prepare($sql); | |
| $stmt->execute(['id' => $postId]); | |
| $pdo->commit(); | |
| return true; | |
| } catch (Exception $e) { | |
| $pdo->rollBack(); | |
| error_log("復元エラー: " . $e->getMessage()); | |
| return false; | |
| } | |
| } | |
| // 使用例 | |
| // moveToDeletedTable($pdo, 15, 100); // 投稿ID 15を削除(削除者ID 100) | |
| // restoreFromDeletedTable($pdo, 15); // 投稿ID 15を復元 | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment