PL/SQL by Example / Savepoints
Savepoints are a mechanism within a PL/SQL block that allows you to divide the transaction being executed into partial units, or milestones. You can then easily return to these milestones without losing the entire transaction.
First, let’s define a helper function that returns the salary of a specific employee.
CREATE OR REPLACE FUNCTION sf_ReturnSalary( p_Employee_ID NUMBER ) RETURN NUMBER IS l_Salary NUMBER; BEGIN SELECT salary INTO l_Salary FROM employees WHERE employee_id=p_Employee_ID ; RETURN l_Salary; END; /
And one more that updates the salary.
CREATE OR REPLACE PROCEDURE sp_SetSalary( p_Employee_ID NUMBER , p_Salary NUMBER ) IS BEGIN UPDATE employees SET salary=p_Salary WHERE (employee_id=p_Employee_ID) ; END; /
In the first step, we set the initial salary to 24000
, which starts the transaction. We don’t want to lose this value, no matter what happens later in the code. Therefore, we create a recovery point and name it outer_savepoint
.
The second step sets the salary again, this time to 25500
.
For illustration, an anonymous block follows, which creates “its own” milestone named inner_savepoint
before making changes, sets the salary to 26000
, and raises an exception. This is caught, and a partial rollback of changes is performed to the state before the anonymous block began.
In the final phase, an exception is raised again, and a rollback to the savepoint occurs, which also nullifies the salary update from the second step.
DECLARE L_EMPLOYEE_ID CONSTANT NUMBER:=100; -- test employee CUSTOM_EXCEPTION EXCEPTION; BEGIN sp_SetSalary(L_EMPLOYEE_ID, 24000); dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 24000 SAVEPOINT outer_savepoint; sp_SetSalary(L_EMPLOYEE_ID, 25500); dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 25500 /* START OF INNER BLOCK */ BEGIN SAVEPOINT inner_savepoint; sp_SetSalary(L_EMPLOYEE_ID, 26000); dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 26000 RAISE CUSTOM_EXCEPTION; EXCEPTION WHEN CUSTOM_EXCEPTION THEN ROLLBACK TO SAVEPOINT inner_savepoint; END; /* END OF INNER BLOCK */ dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 25500 RAISE CUSTOM_EXCEPTION; EXCEPTION WHEN CUSTOM_EXCEPTION THEN ROLLBACK TO SAVEPOINT outer_savepoint; dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 24000 END; /