【Oracle】TCL語言

在這里插入圖片描述

個人主頁:Guiat
歸屬專欄:Oracle

在這里插入圖片描述

文章目錄

  • 1. TCL概述
    • 1.1 什么是TCL?
    • 1.2 TCL的核心功能
  • 2. 事務基礎概念
    • 2.1 事務的ACID特性
    • 2.2 事務的生命周期
  • 3. COMMIT語句詳解
    • 3.1 COMMIT基礎語法
    • 3.2 自動提交與手動提交
    • 3.3 提交性能優化
  • 4. ROLLBACK語句詳解
    • 4.1 ROLLBACK基礎語法
    • 4.2 異常處理中的ROLLBACK
  • 5. SAVEPOINT保存點詳解
    • 5.1 SAVEPOINT基礎概念
    • 5.2 復雜的SAVEPOINT應用
    • 5.3 SAVEPOINT在批處理中的應用
  • 6. SET TRANSACTION語句
    • 6.1 事務隔離級別
    • 6.2 事務名稱和屬性設置

正文
TCL(Transaction Control Language)是Oracle數據庫的"交通指揮官",專門負責管理數據庫事務的流程控制。如果說數據庫是一個繁忙的城市,那TCL就是那個指揮交通、確保秩序的紅綠燈系統。它決定了什么時候讓數據變更"通行"(提交),什么時候要"剎車"(回滾),是保證數據一致性和完整性的關鍵!

1. TCL概述

1.1 什么是TCL?

TCL就像是數據庫的"時間管理大師",它控制著數據變更的節奏和時機。在Oracle這個數據王國里,TCL確保每個數據變更都有始有終,要么完美收官,要么干凈利落地撤銷,絕不留下半吊子的狀態。

TCL事務控制語言
COMMIT提交
ROLLBACK回滾
SAVEPOINT保存點
SET TRANSACTION設置
永久保存變更
釋放鎖資源
撤銷變更
回到起始點
設置檢查點
部分回滾
隔離級別
事務屬性

1.2 TCL的核心功能

Oracle TCL的功能體系就像一個完整的時間管理系統:

Oracle TCL功能體系
事務控制
鎖管理
一致性控制
并發控制
恢復機制
性能優化
開始事務
提交事務
回滾事務
保存點管理
行級鎖
表級鎖
死鎖檢測
鎖等待
讀一致性
寫一致性
ACID特性
多版本控制
隔離級別
并發訪問
沖突處理
事務排隊
自動恢復
手動恢復
實例恢復
介質恢復
批量提交
異步提交
事務分組
資源優化

2. 事務基礎概念

2.1 事務的ACID特性

事務就像是一份"保險合同",必須滿足四個核心特性:

ACID事務特性
原子性 Atomicity
一致性 Consistency
隔離性 Isolation
持久性 Durability
全部成功或全部失敗
不可分割的操作單元
要么做要么不做
數據完整性約束
業務規則一致性
前后狀態一致
事務間相互獨立
并發控制機制
不同隔離級別
提交后永久保存
系統故障不丟失
持久化存儲

2.2 事務的生命周期

-- 事務的典型生命周期演示
BEGIN-- 事務自動開始(第一個DML語句)DBMS_OUTPUT.PUT_LINE('事務開始時間: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));-- 第一個DML操作,事務正式開始INSERT INTO transaction_log (log_id, operation_type, start_time, status)VALUES (txn_log_seq.NEXTVAL, 'DEMO_TRANSACTION', SYSTIMESTAMP, 'STARTED');-- 設置保存點SAVEPOINT operation_1_complete;DBMS_OUTPUT.PUT_LINE('保存點設置: operation_1_complete');-- 執行多個操作UPDATE account_balance SET balance = balance - 1000, last_updated = SYSTIMESTAMPWHERE account_id = 12345;UPDATE account_balance SET balance = balance + 1000, last_updated = SYSTIMESTAMPWHERE account_id = 67890;SAVEPOINT transfer_complete;DBMS_OUTPUT.PUT_LINE('保存點設置: transfer_complete');-- 記錄操作日志INSERT INTO audit_trail (audit_id, transaction_type, amount, from_account, to_account, timestamp)VALUES (audit_seq.NEXTVAL, 'TRANSFER', 1000, 12345, 67890, SYSTIMESTAMP);-- 模擬業務驗證DECLAREv_from_balance NUMBER;v_to_balance NUMBER;BEGINSELECT balance INTO v_from_balance FROM account_balance WHERE account_id = 12345;SELECT balance INTO v_to_balance FROM account_balance WHERE account_id = 67890;IF v_from_balance < 0 THENDBMS_OUTPUT.PUT_LINE('驗證失敗: 賬戶余額不足');ROLLBACK TO transfer_complete;RAISE_APPLICATION_ERROR(-20001, '賬戶余額不足');ELSEDBMS_OUTPUT.PUT_LINE('驗證通過: 轉賬成功');END IF;END;-- 提交事務COMMIT;DBMS_OUTPUT.PUT_LINE('事務提交時間: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('事務異常: ' || SQLERRM);ROLLBACK;DBMS_OUTPUT.PUT_LINE('事務回滾時間: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));RAISE;
END;
/

3. COMMIT語句詳解

3.1 COMMIT基礎語法

COMMIT就像是給所有變更蓋上"官方印章",讓它們永久生效:

-- 基本COMMIT語法
BEGININSERT INTO employees (employee_id, first_name, last_name, hire_date)VALUES (1001, '張', '三', SYSDATE);UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;DELETE FROM employees WHERE status = 'TERMINATED' AND termination_date < ADD_MONTHS(SYSDATE, -24);-- 提交所有變更COMMIT;DBMS_OUTPUT.PUT_LINE('所有變更已永久保存');
END;
/-- 帶注釋的COMMIT
BEGINUPDATE product_prices SET price = price * 1.05 WHERE category = 'ELECTRONICS';INSERT INTO price_change_log (change_id, change_date, category, change_type, change_percent)VALUES (price_log_seq.NEXTVAL, SYSDATE, 'ELECTRONICS', 'INCREASE', 5);-- 提交并添加注釋(在Oracle中,注釋主要用于日志記錄)COMMIT /* 電子產品價格調整 - 漲價5% */;DBMS_OUTPUT.PUT_LINE('價格調整已生效');
END;
/-- 條件COMMIT
DECLAREv_processed_count NUMBER := 0;v_error_count NUMBER := 0;v_batch_size NUMBER := 1000;
BEGINFOR rec IN (SELECT employee_id FROM temp_salary_adjustments) LOOPBEGINUPDATE employees SET salary = salary * 1.08, last_updated = SYSDATEWHERE employee_id = rec.employee_id;v_processed_count := v_processed_count + 1;-- 每處理1000條記錄提交一次IF MOD(v_processed_count, v_batch_size) = 0 THENCOMMIT;DBMS_OUTPUT.PUT_LINE('已處理并提交 ' || v_processed_count || ' 條記錄');END IF;EXCEPTIONWHEN OTHERS THENv_error_count := v_error_count + 1;DBMS_OUTPUT.PUT_LINE('處理員工 ' || rec.employee_id || ' 時出錯: ' || SQLERRM);END;END LOOP;-- 提交剩余的記錄IF MOD(v_processed_count, v_batch_size) != 0 THENCOMMIT;END IF;DBMS_OUTPUT.PUT_LINE('處理完成: ' || v_processed_count || ' 成功, ' || v_error_count || ' 失敗');
END;
/

3.2 自動提交與手動提交

-- 查看當前自動提交設置
SELECT value FROM v$parameter WHERE name = 'autocommit';-- 在SQL*Plus中設置自動提交
-- SET AUTOCOMMIT ON;  -- 每個DML語句后自動提交
-- SET AUTOCOMMIT OFF; -- 手動控制提交(推薦)-- 演示自動提交與手動提交的區別
CREATE OR REPLACE PROCEDURE demo_commit_modes
IS
BEGINDBMS_OUTPUT.PUT_LINE('=== 手動提交模式演示 ===');-- 開始事務INSERT INTO demo_table (id, name, created_date)VALUES (1, '測試數據1', SYSDATE);DBMS_OUTPUT.PUT_LINE('插入數據,但未提交');-- 在另一個會話中,這條數據是不可見的-- 直到執行COMMIT-- 繼續插入更多數據INSERT INTO demo_table (id, name, created_date)VALUES (2, '測試數據2', SYSDATE);INSERT INTO demo_table (id, name, created_date)VALUES (3, '測試數據3', SYSDATE);DBMS_OUTPUT.PUT_LINE('插入了3條數據,仍未提交');-- 統一提交COMMIT;DBMS_OUTPUT.PUT_LINE('所有數據已提交,現在對其他會話可見');
END;
/

3.3 提交性能優化

-- 批量提交優化
CREATE OR REPLACE PROCEDURE optimized_batch_commit(p_batch_size IN NUMBER DEFAULT 10000
)
ISCURSOR data_cursor ISSELECT * FROM large_source_table WHERE processed_flag = 'N';TYPE data_array IS TABLE OF large_source_table%ROWTYPE;l_data_batch data_array;v_total_processed NUMBER := 0;v_start_time TIMESTAMP := SYSTIMESTAMP;v_batch_start_time TIMESTAMP;BEGINDBMS_OUTPUT.PUT_LINE('開始批量處理,批次大小: ' || p_batch_size);OPEN data_cursor;LOOPv_batch_start_time := SYSTIMESTAMP;-- 批量獲取數據FETCH data_cursor BULK COLLECT INTO l_data_batch LIMIT p_batch_size;EXIT WHEN l_data_batch.COUNT = 0;-- 批量處理FORALL i IN 1..l_data_batch.COUNTINSERT INTO target_table (id, data_field1, data_field2, processed_date) VALUES (l_data_batch(i).id,l_data_batch(i).data_field1,l_data_batch(i).data_field2,SYSDATE);-- 更新源表狀態FORALL i IN 1..l_data_batch.COUNTUPDATE large_source_table SET processed_flag = 'Y', processed_date = SYSDATEWHERE id = l_data_batch(i).id;-- 批量提交COMMIT;v_total_processed := v_total_processed + l_data_batch.COUNT;DBMS_OUTPUT.PUT_LINE('批次完成: ' || l_data_batch.COUNT || ' 條記錄, ' ||'累計: ' || v_total_processed || ' 條, ' ||'批次耗時: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_batch_start_time)) || ' 秒');END LOOP;CLOSE data_cursor;DBMS_OUTPUT.PUT_LINE('總計處理: ' || v_total_processed || ' 條記錄, ' ||'總耗時: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_start_time)) || ' 秒');
END;
/-- 異步提交(Oracle 11g+)
BEGININSERT INTO audit_log (log_id, operation, timestamp)VALUES (audit_seq.NEXTVAL, 'SYSTEM_MAINTENANCE', SYSTIMESTAMP);-- 異步提交,不等待日志寫入完成COMMIT WRITE IMMEDIATE NOWAIT;DBMS_OUTPUT.PUT_LINE('異步提交完成,繼續后續操作');
END;
/-- 同步提交(確保日志寫入)
BEGININSERT INTO critical_audit_log (log_id, operation, timestamp)VALUES (critical_audit_seq.NEXTVAL, 'CRITICAL_OPERATION', SYSTIMESTAMP);-- 同步提交,等待日志寫入完成COMMIT WRITE IMMEDIATE WAIT;DBMS_OUTPUT.PUT_LINE('同步提交完成,數據已安全寫入');
END;
/

4. ROLLBACK語句詳解

4.1 ROLLBACK基礎語法

ROLLBACK就像是"時光倒流",讓所有變更回到事務開始前的狀態:

-- 基本ROLLBACK語法
BEGININSERT INTO test_table (id, name) VALUES (1, '測試數據');UPDATE test_table SET name = '修改后的數據' WHERE id = 1;DELETE FROM test_table WHERE id = 999;DBMS_OUTPUT.PUT_LINE('執行了多個DML操作');-- 模擬檢測到錯誤IF 1 = 1 THEN  -- 某種錯誤條件ROLLBACK;DBMS_OUTPUT.PUT_LINE('檢測到錯誤,所有操作已回滾');ELSECOMMIT;DBMS_OUTPUT.PUT_LINE('操作成功,已提交');END IF;
END;
/-- 條件ROLLBACK
DECLAREv_account_balance NUMBER;v_transfer_amount NUMBER := 5000;insufficient_funds EXCEPTION;
BEGIN-- 檢查賬戶余額SELECT balance INTO v_account_balance FROM accounts WHERE account_id = 12345;IF v_account_balance < v_transfer_amount THENRAISE insufficient_funds;END IF;-- 執行轉賬操作UPDATE accounts SET balance = balance - v_transfer_amount WHERE account_id = 12345;UPDATE accounts SET balance = balance + v_transfer_amount WHERE account_id = 67890;INSERT INTO transaction_history (trans_id, from_account, to_account, amount, trans_date) VALUES (trans_seq.NEXTVAL, 12345, 67890, v_transfer_amount, SYSDATE);COMMIT;DBMS_OUTPUT.PUT_LINE('轉賬成功完成');EXCEPTIONWHEN insufficient_funds THENROLLBACK;DBMS_OUTPUT.PUT_LINE('余額不足,轉賬已取消');RAISE_APPLICATION_ERROR(-20001, '賬戶余額不足,無法完成轉賬');WHEN OTHERS THENROLLBACK;DBMS_OUTPUT.PUT_LINE('轉賬過程中發生錯誤: ' || SQLERRM);RAISE;
END;
/-- 在存儲過程中的ROLLBACK
CREATE OR REPLACE PROCEDURE process_order_with_rollback(p_customer_id IN NUMBER,p_product_id IN NUMBER,p_quantity IN NUMBER,p_order_id OUT NUMBER
)
ISv_available_qty NUMBER;v_unit_price NUMBER;v_total_amount NUMBER;inventory_insufficient EXCEPTION;invalid_customer EXCEPTION;
BEGIN-- 驗證客戶DECLAREv_customer_count NUMBER;BEGINSELECT COUNT(*) INTO v_customer_countFROM customersWHERE customer_id = p_customer_id AND status = 'ACTIVE';IF v_customer_count = 0 THENRAISE invalid_customer;END IF;END;-- 檢查庫存SELECT quantity_on_hand, unit_priceINTO v_available_qty, v_unit_priceFROM productsWHERE product_id = p_product_id;IF v_available_qty < p_quantity THENRAISE inventory_insufficient;END IF;-- 計算總金額v_total_amount := p_quantity * v_unit_price;-- 生成訂單SELECT order_seq.NEXTVAL INTO p_order_id FROM dual;INSERT INTO orders (order_id, customer_id, order_date, total_amount, status)VALUES (p_order_id, p_customer_id, SYSDATE, v_total_amount, 'CONFIRMED');INSERT INTO order_items (order_id, product_id, quantity, unit_price, line_total)VALUES (p_order_id, p_product_id, p_quantity, v_unit_price, v_total_amount);-- 更新庫存UPDATE products SET quantity_on_hand = quantity_on_hand - p_quantityWHERE product_id = p_product_id;COMMIT;DBMS_OUTPUT.PUT_LINE('訂單 ' || p_order_id || ' 創建成功');EXCEPTIONWHEN invalid_customer THENROLLBACK;RAISE_APPLICATION_ERROR(-20002, '無效的客戶ID或客戶狀態異常');WHEN inventory_insufficient THENROLLBACK;RAISE_APPLICATION_ERROR(-20003, '庫存不足,當前可用數量: ' || v_available_qty);WHEN OTHERS THENROLLBACK;RAISE_APPLICATION_ERROR(-20004, '訂單創建失敗: ' || SQLERRM);
END;
/

4.2 異常處理中的ROLLBACK

-- 復雜的異常處理和回滾機制
CREATE OR REPLACE PROCEDURE complex_business_transaction
ISv_step VARCHAR2(50);v_processed_count NUMBER := 0;-- 自定義異常step1_error EXCEPTION;step2_error EXCEPTION;step3_error EXCEPTION;BEGINDBMS_OUTPUT.PUT_LINE('開始復雜業務事務處理');-- 第一步:數據驗證和預處理BEGINv_step := 'STEP1_VALIDATION';DBMS_OUTPUT.PUT_LINE('執行步驟: ' || v_step);-- 模擬數據驗證FOR rec IN (SELECT * FROM pending_transactions WHERE status = 'PENDING') LOOPIF rec.amount <= 0 THENRAISE step1_error;END IF;v_processed_count := v_processed_count + 1;END LOOP;DBMS_OUTPUT.PUT_LINE('步驟1完成,驗證了 ' || v_processed_count || ' 條記錄');EXCEPTIONWHEN step1_error THENROLLBACK;RAISE_APPLICATION_ERROR(-20101, '步驟1失敗:數據驗證不通過');WHEN OTHERS THENROLLBACK;RAISE_APPLICATION_ERROR(-20102, '步驟1異常:' || SQLERRM);END;-- 第二步:主要業務邏輯處理BEGINv_step := 'STEP2_PROCESSING';DBMS_OUTPUT.PUT_LINE('執行步驟: ' || v_step);-- 批量處理業務邏輯UPDATE pending_transactions SET status = 'PROCESSING', processing_date = SYSDATEWHERE status = 'PENDING';-- 插入處理記錄INSERT INTO transaction_log (log_id, step_name, processed_count, log_date)VALUES (log_seq.NEXTVAL, v_step, v_processed_count, SYSDATE);DBMS_OUTPUT.PUT_LINE('步驟2完成,處理了 ' || SQL%ROWCOUNT || ' 條記錄');EXCEPTIONWHEN step2_error THENROLLBACK;RAISE_APPLICATION_ERROR(-20201, '步驟2失敗:業務處理錯誤');WHEN OTHERS THENROLLBACK;RAISE_APPLICATION_ERROR(-20202, '步驟2異常:' || SQLERRM);END;-- 第三步:結果確認和清理BEGINv_step := 'STEP3_FINALIZATION';DBMS_OUTPUT.PUT_LINE('執行步驟: ' || v_step);-- 最終確認UPDATE pending_transactions SET status = 'COMPLETED', completion_date = SYSDATEWHERE status = 'PROCESSING';-- 清理臨時數據DELETE FROM temp_processing_data WHERE created_date < SYSDATE - 1;DBMS_OUTPUT.PUT_LINE('步驟3完成,清理了 ' || SQL%ROWCOUNT || ' 條臨時數據');EXCEPTIONWHEN step3_error THENROLLBACK;RAISE_APPLICATION_ERROR(-20301, '步驟3失敗:最終確認錯誤');WHEN OTHERS THENROLLBACK;RAISE_APPLICATION_ERROR(-20302, '步驟3異常:' || SQLERRM);END;-- 所有步驟成功,提交事務COMMIT;DBMS_OUTPUT.PUT_LINE('復雜業務事務處理成功完成');EXCEPTIONWHEN OTHERS THENROLLBACK;DBMS_OUTPUT.PUT_LINE('事務在步驟 ' || v_step || ' 失敗,已全部回滾');-- 記錄錯誤日志INSERT INTO error_log (error_id, error_step, error_message, error_date)VALUES (error_seq.NEXTVAL, v_step, SQLERRM, SYSDATE);COMMIT;  -- 單獨提交錯誤日志RAISE;
END;
/

5. SAVEPOINT保存點詳解

5.1 SAVEPOINT基礎概念

SAVEPOINT就像是游戲中的"存檔點",可以在事務中設置多個檢查點:

SAVEPOINT工作機制
設置保存點
回滾到保存點
釋放保存點
嵌套保存點
SAVEPOINT name
標記當前位置
可設置多個
ROLLBACK TO name
部分回滾
保留后續操作
COMMIT自動釋放
ROLLBACK自動釋放
手動管理
保存點層次
覆蓋同名保存點
靈活回滾控制
-- 基本SAVEPOINT使用
DECLAREv_operation_step NUMBER := 0;
BEGINDBMS_OUTPUT.PUT_LINE('=== SAVEPOINT演示開始 ===');-- 第一步操作v_operation_step := 1;INSERT INTO demo_table (id, step, description, created_date)VALUES (1, v_operation_step, '第一步操作', SYSDATE);SAVEPOINT step1_complete;DBMS_OUTPUT.PUT_LINE('步驟1完成,設置保存點: step1_complete');-- 第二步操作v_operation_step := 2;INSERT INTO demo_table (id, step, description, created_date)VALUES (2, v_operation_step, '第二步操作', SYSDATE);UPDATE demo_table SET description = description || ' - 已更新' WHERE step = 1;SAVEPOINT step2_complete;DBMS_OUTPUT.PUT_LINE('步驟2完成,設置保存點: step2_complete');-- 第三步操作(模擬出錯)v_operation_step := 3;BEGININSERT INTO demo_table (id, step, description, created_date)VALUES (3, v_operation_step, '第三步操作', SYSDATE);-- 模擬業務邏輯錯誤IF SYSDATE > DATE '1900-01-01' THENRAISE_APPLICATION_ERROR(-20001, '模擬的業務錯誤');END IF;SAVEPOINT step3_complete;DBMS_OUTPUT.PUT_LINE('步驟3完成,設置保存點: step3_complete');EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('步驟3發生錯誤: ' || SQLERRM);ROLLBACK TO step2_complete;DBMS_OUTPUT.PUT_LINE('已回滾到保存點: step2_complete');END;-- 第四步操作(替代第三步)v_operation_step := 4;INSERT INTO demo_table (id, step, description, created_date)VALUES (4, v_operation_step, '第四步操作(替代第三步)', SYSDATE);SAVEPOINT step4_complete;DBMS_OUTPUT.PUT_LINE('步驟4完成,設置保存點: step4_complete');-- 提交所有成功的操作COMMIT;DBMS_OUTPUT.PUT_LINE('=== 事務提交,所有保存點自動釋放 ===');END;
/-- 查看最終結果
SELECT id, step, description, created_date 
FROM demo_table 
ORDER BY id;

5.2 復雜的SAVEPOINT應用

-- 多層次保存點管理
CREATE OR REPLACE PROCEDURE multi_level_savepoint_demo
ISv_level1_success BOOLEAN := FALSE;v_level2_success BOOLEAN := FALSE;v_level3_success BOOLEAN := FALSE;
BEGINDBMS_OUTPUT.PUT_LINE('開始多層次保存點演示');-- 第一層操作BEGINDBMS_OUTPUT.PUT_LINE('執行第一層操作...');INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 1, 'LEVEL1_OPERATION', 'STARTED', SYSTIMESTAMP);-- 模擬一些復雜操作FOR i IN 1..5 LOOPINSERT INTO temp_data (id, data_value, level_num)VALUES (i, 'Level1_Data_' || i, 1);END LOOP;SAVEPOINT level1_complete;v_level1_success := TRUE;DBMS_OUTPUT.PUT_LINE('第一層操作完成,設置保存點: level1_complete');-- 第二層操作BEGINDBMS_OUTPUT.PUT_LINE('執行第二層操作...');INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 2, 'LEVEL2_OPERATION', 'STARTED', SYSTIMESTAMP);-- 更新第一層的數據UPDATE temp_data SET data_value = data_value || '_UPDATED_BY_LEVEL2'WHERE level_num = 1;-- 添加第二層數據FOR i IN 6..10 LOOPINSERT INTO temp_data (id, data_value, level_num)VALUES (i, 'Level2_Data_' || i, 2);END LOOP;SAVEPOINT level2_complete;v_level2_success := TRUE;DBMS_OUTPUT.PUT_LINE('第二層操作完成,設置保存點: level2_complete');-- 第三層操作BEGINDBMS_OUTPUT.PUT_LINE('執行第三層操作...');INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 3, 'LEVEL3_OPERATION', 'STARTED', SYSTIMESTAMP);-- 模擬第三層可能失敗的操作FOR i IN 11..15 LOOPINSERT INTO temp_data (id, data_value, level_num)VALUES (i, 'Level3_Data_' || i, 3);-- 模擬在第13個操作時失敗IF i = 13 THENRAISE_APPLICATION_ERROR(-20003, '第三層操作模擬失敗');END IF;END LOOP;SAVEPOINT level3_complete;v_level3_success := TRUE;DBMS_OUTPUT.PUT_LINE('第三層操作完成,設置保存點: level3_complete');EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('第三層操作失敗: ' || SQLERRM);ROLLBACK TO level2_complete;DBMS_OUTPUT.PUT_LINE('已回滾到保存點: level2_complete');-- 記錄第三層失敗INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 3, 'LEVEL3_OPERATION', 'FAILED', SYSTIMESTAMP);END;EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('第二層操作失敗: ' || SQLERRM);ROLLBACK TO level1_complete;DBMS_OUTPUT.PUT_LINE('已回滾到保存點: level1_complete');INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 2, 'LEVEL2_OPERATION', 'FAILED', SYSTIMESTAMP);END;EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('第一層操作失敗: ' || SQLERRM);ROLLBACK;DBMS_OUTPUT.PUT_LINE('已回滾整個事務');INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)VALUES (log_seq.NEXTVAL, 1, 'LEVEL1_OPERATION', 'FAILED', SYSTIMESTAMP);COMMIT;  -- 只提交錯誤日志RETURN;END;-- 更新操作狀態IF v_level1_success THENUPDATE operation_log SET status = 'COMPLETED' WHERE level_num = 1 AND operation = 'LEVEL1_OPERATION' AND status = 'STARTED';END IF;IF v_level2_success THENUPDATE operation_log SET status = 'COMPLETED' WHERE level_num = 2 AND operation = 'LEVEL2_OPERATION' AND status = 'STARTED';END IF;IF v_level3_success THENUPDATE operation_log SET status = 'COMPLETED' WHERE level_num = 3 AND operation = 'LEVEL3_OPERATION' AND status = 'STARTED';END IF;COMMIT;DBMS_OUTPUT.PUT_LINE('多層次操作完成,最終狀態已提交');-- 輸出最終統計DBMS_OUTPUT.PUT_LINE('操作結果統計:');DBMS_OUTPUT.PUT_LINE('第一層: ' || CASE WHEN v_level1_success THEN '成功' ELSE '失敗' END);DBMS_OUTPUT.PUT_LINE('第二層: ' || CASE WHEN v_level2_success THEN '成功' ELSE '失敗' END);DBMS_OUTPUT.PUT_LINE('第三層: ' || CASE WHEN v_level3_success THEN '成功' ELSE '失敗' END);
END;
/

5.3 SAVEPOINT在批處理中的應用

-- 大批量數據處理中的保存點應用
CREATE OR REPLACE PROCEDURE batch_process_with_savepoints(p_batch_size IN NUMBER DEFAULT 1000,p_max_errors IN NUMBER DEFAULT 10
)
ISCURSOR data_cursor ISSELECT rowid as row_id, id, data_field1, data_field2FROM large_source_tableWHERE processed_flag = 'N'ORDER BY id;TYPE rowid_array IS TABLE OF ROWID;TYPE number_array IS TABLE OF NUMBER;TYPE varchar_array IS TABLE OF VARCHAR2(100);l_row_ids rowid_array;l_ids number_array;l_data1 varchar_array;l_data2 varchar_array;v_batch_count NUMBER := 0;v_total_processed NUMBER := 0;v_total_errors NUMBER := 0;v_current_batch_errors NUMBER := 0;BEGINDBMS_OUTPUT.PUT_LINE('開始批量處理,批次大小: ' || p_batch_size);DBMS_OUTPUT.PUT_LINE('最大錯誤容忍數: ' || p_max_errors);OPEN data_cursor;LOOP-- 批量獲取數據FETCH data_cursor BULK COLLECT INTO l_row_ids, l_ids, l_data1, l_data2LIMIT p_batch_size;EXIT WHEN l_row_ids.COUNT = 0;v_batch_count := v_batch_count + 1;v_current_batch_errors := 0;DBMS_OUTPUT.PUT_LINE('處理第 ' || v_batch_count || ' 批,記錄數: ' || l_row_ids.COUNT);-- 設置批次開始保存點SAVEPOINT batch_start;-- 逐條處理當前批次FOR i IN 1..l_row_ids.COUNT LOOPBEGIN-- 設置單條記錄保存點SAVEPOINT record_start;-- 復雜的業務處理邏輯INSERT INTO target_table1 (id, processed_data, created_date)VALUES (l_ids(i), UPPER(l_data1(i)), SYSDATE);INSERT INTO target_table2 (source_id, calculated_value, created_date)VALUES (l_ids(i), LENGTH(l_data2(i)) * 10, SYSDATE);-- 更新源表狀態UPDATE large_source_tableSET processed_flag = 'Y', processed_date = SYSDATEWHERE rowid = l_row_ids(i);v_total_processed := v_total_processed + 1;EXCEPTIONWHEN OTHERS THEN-- 單條記錄處理失敗,回滾到記錄開始ROLLBACK TO record_start;v_current_batch_errors := v_current_batch_errors + 1;v_total_errors := v_total_errors + 1;DBMS_OUTPUT.PUT_LINE('記錄 ' || l_ids(i) || ' 處理失敗: ' || SQLERRM);-- 記錄錯誤信息INSERT INTO error_log (error_id, source_id, error_message, error_date, batch_number) VALUES (error_seq.NEXTVAL, l_ids(i), SQLERRM, SYSDATE, v_batch_count);-- 檢查是否超過錯誤容忍限制IF v_total_errors > p_max_errors THENDBMS_OUTPUT.PUT_LINE('錯誤數超過限制,終止處理');ROLLBACK TO batch_start;CLOSE data_cursor;RAISE_APPLICATION_ERROR(-20100, '錯誤數超過限制: ' || p_max_errors);END IF;END;END LOOP;-- 檢查當前批次錯誤率IF v_current_batch_errors > l_row_ids.COUNT * 0.1 THENDBMS_OUTPUT.PUT_LINE('當前批次錯誤率過高,回滾整個批次');ROLLBACK TO batch_start;ELSE-- 提交當前批次COMMIT;DBMS_OUTPUT.PUT_LINE('第 ' || v_batch_count || ' 批處理完成,' ||'成功: ' || (l_row_ids.COUNT - v_current_batch_errors) || ',' ||'失敗: ' || v_current_batch_errors);END IF;END LOOP;CLOSE data_cursor;DBMS_OUTPUT.PUT_LINE('批量處理完成');DBMS_OUTPUT.PUT_LINE('總計處理: ' || v_total_processed || ' 條記錄');DBMS_OUTPUT.PUT_LINE('總計錯誤: ' || v_total_errors || ' 條記錄');DBMS_OUTPUT.PUT_LINE('處理批次: ' || v_batch_count || ' 個批次');END;
/

6. SET TRANSACTION語句

6.1 事務隔離級別

SET TRANSACTION就像是給事務設定"行為準則":

Oracle隔離級別
READ COMMITTED
SERIALIZABLE
READ ONLY
默認級別
語句級讀一致性
避免臟讀
允許不可重復讀
事務級讀一致性
避免幻象讀
最高隔離級別
可能降低并發性
只讀事務
不允許DML
查詢一致性快照
適合報表查詢
-- READ COMMITTED隔離級別(默認)
BEGIN-- 顯式設置READ COMMITTED(通常不需要,因為是默認值)SET TRANSACTION ISOLATION LEVEL READ COMMITTED;DBMS_OUTPUT.PUT_LINE('使用READ COMMITTED隔離級別');-- 在這個級別下,每個查詢都能看到查詢開始時已提交的數據SELECT COUNT(*) as current_count FROM employees;-- 如果其他會話在此時插入并提交了新記錄,下一個查詢會看到它們DBMS_LOCK.SLEEP(5);  -- 等待5秒,模擬其他會話操作SELECT COUNT(*) as updated_count FROM employees;COMMIT;
END;
/-- SERIALIZABLE隔離級別
BEGINSET TRANSACTION ISOLATION LEVEL SERIALIZABLE;DBMS_OUTPUT.PUT_LINE('使用SERIALIZABLE隔離級別');-- 記錄事務開始時的SCNDECLAREv_start_scn NUMBER;v_emp_count1 NUMBER;v_emp_count2 NUMBER;BEGINSELECT CURRENT_SCN INTO v_start_scn FROM v$database;DBMS_OUTPUT.PUT_LINE('事務開始SCN: ' || v_start_scn);SELECT COUNT(*) INTO v_emp_count1 FROM employees;DBMS_OUTPUT.PUT_LINE('第一次查詢員工數: ' || v_emp_count1);-- 即使其他會話提交了新數據,在SERIALIZABLE模式下-- 本事務仍然只能看到事務開始時的數據快照DBMS_LOCK.SLEEP(10);SELECT COUNT(*) INTO v_emp_count2 FROM employees;DBMS_OUTPUT.PUT_LINE('第二次查詢員工數: ' || v_emp_count2);IF v_emp_count1 = v_emp_count2 THENDBMS_OUTPUT.PUT_LINE('SERIALIZABLE確保了讀一致性');END IF;END;COMMIT;
END;
/-- READ ONLY事務
BEGINSET TRANSACTION READ ONLY;DBMS_OUTPUT.PUT_LINE('只讀事務開始');-- 生成一致性報表DECLAREv_report_time TIMESTAMP := SYSTIMESTAMP;BEGINDBMS_OUTPUT.PUT_LINE('報表生成時間: ' || TO_CHAR(v_report_time, 'YYYY-MM-DD HH24:MI:SS'));-- 部門統計FOR dept_rec IN (SELECT d.department_name, COUNT(e.employee_id) as emp_count, AVG(e.salary) as avg_salaryFROM departments dLEFT JOIN employees e ON d.department_id = e.department_idGROUP BY d.department_nameORDER BY d.department_name) LOOPDBMS_OUTPUT.PUT_LINE('部門: ' || dept_rec.department_name || ', 員工數: ' || dept_rec.emp_count || ', 平均薪資: ' || ROUND(dept_rec.avg_salary, 2));END LOOP;-- 在只讀事務中,所有查詢都看到同一個時間點的數據快照-- 確保報表數據的一致性-- 嘗試執行DML會失敗BEGININSERT INTO test_table VALUES (1, '測試');EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('只讀事務中不能執行DML: ' || SQLERRM);END;END;COMMIT;  -- 或者 ROLLBACK,對只讀事務效果相同
END;
/

6.2 事務名稱和屬性設置

-- 設置事務名稱(便于監控和調試)
BEGINSET TRANSACTION NAME '月度財務結算';DBMS_OUTPUT.PUT_LINE('開始執行月度財務結算事務');-- 復雜的財務處理邏輯INSERT INTO monthly_summary (month_year, total_revenue, total_expenses, net_profit)SELECT TO_CHAR(SYSDATE, 'YYYY-MM'),SUM(CASE WHEN transaction_type = 'REVENUE' THEN amount ELSE 0 END),SUM(CASE WHEN transaction_type = 'EXPENSE' THEN amount ELSE 0 END),SUM(CASE WHEN transaction_type = 'REVENUE' THEN amount ELSE -amount END)FROM financial_transactionsWHERE transaction_date >= TRUNC(SYSDATE, 'MM')AND transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);-- 更新賬戶余額UPDATE account_balances abSET current_balance = (SELECT ab.current_balance + COALESCE(SUM(CASE WHEN ft.transaction_type = 'CREDIT' THEN ft.amount ELSE -ft.amount END), 0)FROM financial_transactions ftWHERE ft.account_id = ab.account_idAND ft.transaction_date >= TRUNC(SYSDATE, 'MM')AND ft.transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1)AND ft.processed_flag = 'N');-- 標記已處理的交易UPDATE financial_transactions SET processed_flag = 'Y', processed_date = SYSDATEWHERE transaction_date >= TRUNC(SYSDATE, 'MM')AND transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1)AND processed_flag = 'N';COMMIT;DBMS_OUTPUT.PUT_LINE('月度財務結算完成');
END;
/-- 設置事務使用特定回滾段
BEGIN-- 在舊版本Oracle中可以指定回滾段(現在通常由Oracle自動管理)-- SET TRANSACTION USE ROLLBACK SEGMENT rbs_large;DBMS_OUTPUT.PUT_LINE('開始大型數據操作事務');-- 大批量數據操作INSERT INTO archive_tableSELECT * FROM active_tableWHERE created_date < ADD_MONTHS(SYSDATE, -24);DELETE FROM active_tableWHERE created_date < ADD_MONTHS(SYSDATE, -24);COMMIT;DBMS_OUTPUT.PUT_LINE('數據歸檔操作完成');
END;
/-- 組合使用多個事務屬性
BEGINSET TRANSACTION ISOLATION LEVEL SERIALIZABLENAME '關鍵業務一致性檢查';DBMS_OUTPUT.PUT_LINE('開始關鍵業務一致性檢查');-- 在SERIALIZABLE級別下執行一致性檢查DECLAREv_accounts_total NUMBER;v_transactions_total NUMBER;v_difference NUMBER;BEGIN-- 計算所有賬戶余額總和SELECT SUM(balance) INTO v_accounts_total FROM account_balances;-- 計算所有交易凈額總和SELECT SUM(CASE WHEN transaction_type = 'CREDIT' THEN amount ELSE -amount END)INTO v_transactions_total FROM all_transactions;v_difference := ABS(v_accounts_total - v_transactions_total);DBMS_OUTPUT.PUT_LINE('賬戶余額總和: ' || v_accounts_total);DBMS_OUTPUT.PUT_LINE('交易凈額總和: ' || v_transactions_total);DBMS_OUTPUT.PUT_LINE('差額: ' || v_difference);IF v_difference > 0.01 THEN  -- 允許1分錢的舍入誤差RAISE_APPLICATION_ERROR(-20200, '數據不一致,差額: ' || v_difference);ELSEDBMS_OUTPUT.PUT_LINE('一致性檢查通過');END IF;END;COMMIT;
END;
/

結語
感謝您的閱讀!期待您的一鍵三連!歡迎指正!

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83121.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83121.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83121.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenCV CUDA模塊直方圖計算------用于在 GPU 上執行對比度受限的自適應直方圖均衡類cv::cuda::CLAHE

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模塊中提供的一個類&#xff0c;用于在 GPU 上執行對比度受限的自適應直方圖均衡&#xff08;Contrast Limi…

OpenGAN:基于開放數據生成的開放集識別

簡介 簡介&#xff1a;這次學習的OpenGAN主要學習一個思路&#xff0c;跳出傳統GAN對于判斷真假的識別到判斷是已知種類還是未知種類。重點內容不在于代碼而是思路&#xff0c;會簡要給出一個設計的代碼。 論文題目&#xff1a;OpenGAN: Open-Set Recognition via Open Data …

隨機游動算法解決kSAT問題

input&#xff1a;n個變量的k-CNF公式 ouput&#xff1a;該公式的一組滿足賦值或宣布沒有滿足賦值 算法步驟&#xff1a; 隨機均勻地初始化賦值 a ∈ { 0 , 1 } n a\in\{0,1\}^n a∈{0,1}n.重復t次&#xff08;后面會估計這個t&#xff09;&#xff1a; a. 如果在當前賦值下…

企業上線ESOP電子作業指導書系統實現車間無紙化的投入收益數據綜合分析

企業上線ESOP電子作業指導書系統實現車間無紙化的投入收益數據綜合分析 一、成本節約&#xff1a;無紙化直接降低運營成本 紙張與耗材費用銳減 o 杭州科創致遠案例&#xff1a;某汽配企業引入無紙化系統后&#xff0c;年節省紙張耗材費用超50萬元。通過電子化替代傳統紙質文檔…

高并發抽獎系統優化方案

引子 最近接觸了一個抽獎的項目&#xff0c;由于用戶量比較大&#xff0c;而且第三方提供的認證接口并發量有限&#xff0c;為了保證服務的高可用性&#xff0c;所以對高并限制發有一定的要求。經過一系列研究和討論&#xff0c;做出了以下一些優化方案。 需求分析 根據用戶量…

STM32八股【10】-----stm32啟動流程

啟動流程 1.上電復位 2.系統初始化 3.跳轉到 main 函數 啟動入口&#xff1a; cpu被清空&#xff0c;程序從0x00000000開始運行0x00000000存放的是reset_handler的入口地址0x00000000的實際位置會變&#xff0c;根據不同的啟動模式決定啟動模式分為&#xff1a; flash啟動&a…

LLMTIME: 不用微調!如何用大模型玩轉時間序列預測?

今天是端午節&#xff0c;端午安康&#xff01;值此傳統佳節之際&#xff0c;我想和大家分享一篇關于基于大語言模型的時序預測算法——LLMTIME。隨著人工智能技術的飛速發展&#xff0c;利用大型預訓練語言模型&#xff08;LLM&#xff09;進行時間序列預測成為一個新興且極具…

在VirtualBox中打造高效開發環境:CentOS虛擬機安裝與優化指南

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 一、為何選擇VirtualBox CentOS組合&#xff1f; 對于程序員而言&#xff0c;構建隔離的開發測試環境是剛需。VirtualBox憑借其跨平臺支持&#xff08;W…

LeeCode 98. 驗證二叉搜索樹

給你一個二叉樹的根節點 root &#xff0c;判斷其是否是一個有效的二叉搜索樹。 有效 二叉搜索樹定義如下&#xff1a; 節點的左子樹只包含 小于 當前節點的數。節點的右子樹只包含 大于 當前節點的數。所有左子樹和右子樹自身必須也是二叉搜索樹。 提示&#xff1a; 樹中節…

Python簡易音樂播放器開發教程

&#x1f4da; 前言 編程基礎第一期《12-30》–音樂播放器是日常生活中常用的應用程序&#xff0c;使用Python和pygame庫可以輕松實現一個簡易的音樂播放器。本教程將詳細講解如何開發一個具有基本功能的音樂播放器&#xff0c;并解析其中涉及的Python編程知識點。 &#x1f6e…

ssh連接斷開,保持任務后臺執行——tmux

目錄 **核心用途****基礎使用方法**1. **安裝 tmux**2. **啟動新會話**3. **常用快捷鍵&#xff08;需先按 Ctrlb 前綴&#xff09;**4. **會話管理命令**5. **窗格操作進階** **典型工作流****注意事項****配置文件&#xff08;~/.tmux.conf&#xff09;** tmux&#xff08; …

3D Gaussian splatting 04: 代碼閱讀-提取相機位姿和稀疏點云

目錄 3D Gaussian splatting 01: 環境搭建3D Gaussian splatting 02: 快速評估3D Gaussian splatting 03: 用戶數據訓練和結果查看3D Gaussian splatting 04: 代碼閱讀-提取相機位姿和稀疏點云3D Gaussian splatting 05: 代碼閱讀-訓練整體流程3D Gaussian splatting 06: 代碼…

每日c/c++題 備戰藍橋杯(P1204 [USACO1.2] 擠牛奶 Milking Cows)

P1204 [USACO1.2] 擠牛奶 Milking Cows - 詳解與代碼實現 一、題目背景 三個農民每天清晨[……]&#xff08;簡要介紹題目背景&#xff0c;與官網描述類似&#xff09; 二、問題分析 輸入要求 &#xff1a;讀取 N 個農民的擠奶時間區間&#xff0c;計算兩個值&#xff1a;最…

保持本地 Git 項目副本與遠程倉庫完全同步

核心目標&#xff1a; 保持本地 Git 項目副本與 GitHub 遠程倉庫完全同步。 關鍵方法&#xff1a; 定期執行 git pull 命令。 操作步驟&#xff1a; 進入項目目錄&#xff1a; 在終端/命令行中&#xff0c;使用 cd 命令切換到你的項目文件夾。執行拉取命令&#xff1a; 運行…

Flutter 4.x 版本 webview_flutter 嵌套H5

踩坑早期版本 使用 WebView 代碼如下 import package:flutter/material.dart; import package:webview_flutter/webview_flutter.dart;class HomePage extends StatelessWidget {const HomePage({super.key});overrideWidget build(BuildContext context) {return Scaffold(ap…

rtpinsertsound:語音注入攻擊!全參數詳細教程!Kali Linux教程!

簡介 2006年8月至9月期間&#xff0c;我們創建了一個用于將音頻插入指定音頻&#xff08;即RTP&#xff09;流的工具。該工具名為rtpinsertsound。 該工具已在Linux Red Hat Fedora Core 4平臺&#xff08;奔騰IV&#xff0c;2.5 GHz&#xff09;上進行了測試&#xff0c;但預…

跑步前熱身動作

跑前熱身的核心目標是升高體溫、激活肌肉、預防損傷 &#xff0c;同時通過動態動作提升運動表現。熱身&#xff08;步驟關節→肌肉→心肺&#xff09;和針對性動作&#xff08;如抱膝抬腿&#xff09;能有效降低受傷風險&#xff0c;建議每次跑步前嚴格執行。 推薦跑前熱身動作…

GIT命令行的一些常規操作

放棄修改 git checkout . 修改commit信息 git commit --amend 撤銷上次本地commit 1、通過git log查看上次提交的哈希值 2、git reset --soft 哈希值 分支 1.創建本地分支 git branch 分支名 2.切換本地分支 git checkout mybranch&#xff1b; 3.創建一個新分支并…

RAGFlow從理論到實戰的檢索增強生成指南

目錄 前言 一、RAGFlow是什么&#xff1f;為何需要它&#xff1f; 二、RAGFlow技術架構拆解 三、實戰指南&#xff1a;從0到1搭建RAGFlow系統 步驟1&#xff1a;環境準備 步驟2&#xff1a;數據接入 步驟3&#xff1a;檢索與生成 四、優化技巧&#xff1a;讓RAGFlow更精…

軟件工程方法論:在確定性與不確定性的永恒之舞中尋找平衡

當我們談論“軟件工程”時&#xff0c;“工程”二字總暗示著某種如橋梁建造般的精確與可控。然而&#xff0c;軟件的本質卻根植于人類思維的復雜性與需求的流變之中。軟件工程方法論的發展史&#xff0c;并非線性進步的凱歌&#xff0c;而是一部在確定性的渴望與不確定性的現實…