生產環境某用戶誤將 TAW_RM_USER.PASSWORD 全表更新為 '123456',距離上次 DMP 備份已過 10 天,期間有新增用戶、資料變更。直接導入備份會丟失 10 天業務增量;不處理則安全合規不通過。本文展示PL/SQL 遊標精準回滾單一欄位方案:僅恢復舊用戶密碼,保留新用戶與其他欄位變更,業務零停機、數據零遺失。
COM (含表 TAW_RM_USER)COMTEST (同結構表 TAW_RM_USER)UPDATE COMTEST.TAW_RM_USER SET PASSWORD = '123456'; (本意僅測試庫,實則連正式庫)TAW_RM_USER 歷史密碼)123456,測試庫正常,10 天內有新用戶註冊、舊用戶資料變更CREATE TABLE comtest.taw_rm_user_bak AS
SELECT * FROM comtest.taw_rm_user;
DROP TABLE comtest.taw_rm_user; -- 或 TRUNCATE TABLE
imp comtest/password@sid FILE=backup_10days_ago.dmp TABLES=TAW_RM_USER IGNORE=Y
導入後 COMTEST.TAW_RM_USER 僅含 10 天前的用戶與密碼 (無新用戶)。
DECLARE
-- 定義記錄型別對應測試庫歷史表
rec_hist comtest.taw_rm_user%ROWTYPE;
-- 遊標:讀取歷史密碼
CURSOR cur_hist IS
SELECT user_id, password -- 僅選擇必要欄位,減少網絡傳輸
FROM comtest.taw_rm_user;
BEGIN
OPEN cur_hist;
LOOP
FETCH cur_hist INTO rec_hist;
EXIT WHEN cur_hist%NOTFOUND;
-- 僅更新正式庫中「存在該用戶」的密碼 (保留新用戶不變)
UPDATE com.taw_rm_user
SET password = rec_hist.password
WHERE user_id = rec_hist.user_id;
-- 批次提交控制 (每 1000 行提交,避免大事務)
IF cur_hist%ROWCOUNT MOD 1000 = 0 THEN
COMMIT;
END IF;
END LOOP;
CLOSE cur_hist;
COMMIT; -- 最終提交
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('回滾失敗: ' || SQLERRM);
RAISE;
END;
/
| 設計點 | 實現方式 | 業務價值 |
|---|---|---|
| 精準匹配 | WHERE user_id = rec_hist.user_id | 僅更新歷史用戶,新用戶 (歷史表無記錄) 自動跳過 |
| 單欄位更新 | SET password = ... | 不觸及其他欄位 (姓名、郵箱、狀態等 10 天變更保留) |
| 批次提交 | MOD 1000 COMMIT | 控制 UNDO 表空間、鎖持有時間、減少失敗回滾範圍 |
| 異常保護 | EXCEPTION WHEN OTHERS ROLLBACK | 任一行失敗全量回滾,保證原子性 |
| 最小權限 | 僅需 COMTEST.TAW_RM_USER SELECT、COM.TAW_RM_USER UPDATE | 符合最小權限原則,降低風險 |
DROP TABLE comtest.taw_rm_user;
CREATE TABLE comtest.taw_rm_user AS SELECT * FROM comtest.taw_rm_user_bak;
DROP TABLE comtest.taw_rm_user_bak;
SELECT COUNT(*) FROM com.taw_rm_user WHERE password != '123456'; → 應等於歷史用戶數SELECT COUNT(*) FROM com.taw_rm_user WHERE user_id NOT IN (SELECT user_id FROM comtest.taw_rm_user); → 新用戶數,密碼應仍為 123456 (或其實際設置值)123456 登錄成功此方案核心模式:歷史數據源 + 遊標逐行精準 UPDATE + 業務鍵匹配 + 批次提交 + 異常回滾,適用於任意「單欄位誤更新、需保留增量業務變更」場景:
請將此 PL/SQL 模板納入團隊「數據修復工具箱」,下次面對「誤 UPDATE 全表」恐慌時,冷靜執行精準手術——數據修復,也可以是微創的。