MySQL刪除前怎麼確認只中目標列:先用交易與SELECT核範圍

整理網站草稿,最怕 DELETE 的條件少了一項,把別人的資料或已發布內容一起刪掉。先把要刪的範圍寫成 SELECT,檢查實際 ID、狀態與日期,再在支援交易的資料表中試刪、核對列數,最後明確選擇撤回或提交。

這篇使用獨立 MySQL 8.4.9 的 InnoDB 合成資料。交易提供的是提交前的撤回機會;它不會替你判斷哪些內容該刪,也不能在 COMMIT 後直接用 ROLLBACK 找回資料。

先把「舊草稿」寫成完整條件

假設要清理租戶 7、狀態為 draft、建立日期早於 2026-10-01 的資料。這三項共同構成範圍。不要只留下日期,或只看列表頁顯示了某個租戶,就以為資料庫也自動帶入相同限制。

SELECT id, tenant_id, status, created_at
FROM draft_items
WHERE tenant_id = 7
  AND status = 'draft'
  AND created_at < '2026-10-01'
ORDER BY id;

本例五列中,101、102 符合條件;103 已發布,104 屬於另一租戶,105 是截止日期之後的新草稿。這三列都應留下。檢查的不只是「共有兩筆」,還要看兩筆是不是你原本要清理的資料。

日期是否包含當天也要說清楚。小於 10 月 1 日與小於等於 10 月 1 日不同;若欄位是時間戳,還涉及時區與當天的時間界線。範例使用 DATE 欄位,不把這個條件直接套到所有正式資料表。

確認引擎、連線與相關資料

先由維護者核對表名、所在資料庫和 storage engine。這份案例使用 InnoDB;不能假定任何資料表的 DELETE 都能由交易撤回。也確認有沒有外鍵、觸發器或其他引用,刪除兩列可能不只影響畫面上的兩個項目。

正式清理前仍需要可還原的備份與範圍批准。先在自有測試資料核對流程,再由有權限的維護者安排正式操作。交易不代替備份,尤其是提交後、連線中斷或操作工具自動處理交易時。

START TRANSACTION、查詢、刪除與撤回要在同一個資料庫連線內執行。某些工具每次點執行會換連線,或自行提交;先查工具的交易模式,不要開一個新分頁就認為沿用上一個交易。

先走撤回分支,確認刪到哪兩列

範例在交易內使用 SELECT FOR UPDATE 讀取目標,再以完全相同的 WHERE 刪除。鎖定讀取會影響其他連線,應在準備好條件後才開始,迅速核對並結束,不要開著交易去開會或等人工長時間審核。

START TRANSACTION;

SELECT id, tenant_id, status, created_at
FROM draft_items
WHERE tenant_id = 7
  AND status = 'draft'
  AND created_at < '2026-10-01'
ORDER BY id FOR UPDATE;

DELETE FROM draft_items
WHERE tenant_id = 7
  AND status = 'draft'
  AND created_at < '2026-10-01';

SELECT ROW_COUNT() AS deleted_rows;
SELECT id FROM draft_items ORDER BY id;

ROLLBACK;
SELECT id FROM draft_items ORDER BY id;

在本例,鎖定讀取列出 101、102,DELETE 後立即讀取的 ROW_COUNT() 是 2。同一連線在交易中只見 103、104、105;執行 ROLLBACK 後,再查回到 101 至 105 共五列。這證明這次未提交的兩列刪除已撤回。

ROW_COUNT() 應緊接在 DELETE 後查看,其他語句可能改變它代表的結果。實際 ID 清單與列數一起保留,才能辨認「刪兩筆」是否真的等於「刪對兩筆」。

提交是另一個明確決定

撤回演練完成後,這份合成案例另開新交易,重新讀取同一條件、刪除同樣兩列,確認仍只剩另外三列,再執行 COMMIT。提交後重新查詢,留下的 ID 是 103、104、105。

-- 僅在本次範圍及結果已確認,並獲准提交時
COMMIT;

不能在已撤回的交易尾端補一個 COMMIT,期望先前刪除重新出現;也不能在已提交後補 ROLLBACK,期望資料回復。兩個分支的結束方式不同,操作記錄要保存你實際執行了哪一個。

若查到的 ID、數量或引用關係與預期不同,在尚未提交的適用交易內撤回,查明原因後重新制定條件。不要臨時加 LIMIT,把錯誤條件縮成幾列就當作解決;少刪不代表刪對。

把留下的資料也列入確認

只看刪除目標,可能忽略應保留的資料。本例另外放入已發布列、另一租戶列與新草稿,讓每一項條件都有反例。自己的測試表也應包含這些邊界,而不是全部都放成符合條件的舊草稿。

演練後分別核對目標與反例:撤回分支應兩組都存在;提交分支應只移除已批准的目標,反例仍完整。若資料被其他表引用,也要查引用是否符合既定規則,不能只用主表總列數判定整理完成。

把 SELECT 的條件直接對照 DELETE,特別留意括號、租戶限制和截止日期。條件從管理介面傳入時,使用既有參數化查詢與權限流程;不要把訪客提供的任意字串拼進清理 SQL。這份固定合成條件只示範核對範圍,不是可對外開放的刪除入口。

受影響列數超過預期時,先結束尚未提交的適用交易並保存記錄。不要先提交,等回到列表才確認;列表可能有分頁、篩選或快取,看不見的資料也可能已被刪除。資料庫 ID 清單比單一畫面更適合核對這次範圍。

不要把整個網站視為凍結

普通 SELECT 與稍後 DELETE 之間,其他連線可能修改資料。FOR UPDATE 能提供鎖定讀取,但實際鎖定範圍還受索引、查詢與隔離層級影響;本例不能保證任何查詢都會把所有未來新增資料凍結。

案例替租戶、狀態、日期建立索引,並在同一短交易完成。正式資料量大或其他程序持續寫入時,由維護者核對執行計畫和鎖的影響;需要分批清理,也應先定義批次順序與固定目標,不能邊看結果邊任意改 WHERE。

交易之外的動作另行處理

本例只有一張合成表,沒有外鍵、觸發器或外部服務。正式程式若同時刪檔案、寄信或呼叫第三方 API,資料庫 ROLLBACK 不會自動撤回那些動作。先核對清理流程的實際順序與回復方法,再安排執行。

也不要在這段交易中混入 TRUNCATE、ALTER TABLE 等結構操作;部分語句會造成隱式提交。這篇的撤回結果來自 InnoDB 的 DELETE,不能泛化成「開始交易後,任何 SQL 都能反悔」。

最後保留完整條件、目標 ID、受影響列數與提交結果;提交後再核對應保留的資料。若已提交卻發現刪錯,由維護者依備份與既有還原流程處理,不靠反覆執行 ROLLBACK 掩蓋問題。

參考資料

原文鏈接:https://wntheme.com/mysql-delete-transaction-range/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?