how to return updated and previous value through a stored procedure?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A common requirement in database code is to update a row and return both the old value and the new value in the same operation. That usually comes up in audit logging, optimistic UI updates, or APIs that want to confirm what changed without issuing a second query. The safest solution is to capture the old and new versions inside the update statement itself rather than reading once and updating later in separate steps.
Use the Database Engine to Capture Both Versions
If you run a SELECT to get the current value and then perform an UPDATE, another session can change the row in between. That gives you a race condition. A better pattern is to let the database return both images of the row during the update.
In SQL Server, the OUTPUT clause is designed for this. It exposes the pre-update row through deleted and the post-update row through inserted.
Return Previous and Updated Values From a Stored Procedure
For a single-row update, one straightforward pattern is to write the OUTPUT rows into a table variable and then return them at the end of the procedure.
Calling the procedure returns both values from the actual update, not from a separate lookup.
That result set can be consumed directly by application code.
Returning Values With Output Parameters
If you know the procedure affects at most one row and your calling code prefers output parameters, you can still capture the values through the OUTPUT clause and assign them after the update.
This style is useful when the caller already expects scalar outputs.
Handle Missing Rows and Business Rules Explicitly
A stored procedure should make it obvious when no row matched the requested key. Otherwise the caller may see NULL outputs and not know whether that means "not found" or "updated to null".
The Price <> @NewPrice predicate is optional, but it can help distinguish a real change from a no-op.
Why This Pattern Is Better Than Separate Queries
Capturing deleted and inserted values inside the update gives you three advantages:
- it avoids race conditions between read and write
- it works naturally with transaction boundaries
- it scales to multiple changed columns, not just one field
If you later need audit logging, the same OUTPUT data can be inserted into an audit table instead of or in addition to returning it to the caller.
Common Pitfalls
A common mistake is doing SELECT current_value and then UPDATE in separate statements. That looks simple, but it can return stale "previous" values under concurrent writes.
Another problem is using output parameters for a statement that can update multiple rows. Output parameters only represent one scalar value, so a result set or audit table is a better fit when more than one row may change.
Developers also forget to handle the case where no row matched the WHERE clause. That makes error handling ambiguous for the caller.
Finally, keep the solution database-specific. The SQL Server OUTPUT deleted and inserted pattern is excellent here, but other engines use different syntax such as RETURNING or triggers. Do not assume the exact same procedure body is portable across vendors.
Summary
- Capture old and new values inside the
UPDATE, not with separate read and write steps. - In SQL Server, use the
OUTPUTclause withdeletedandinserted. - Return a result set for multi-row changes and output parameters for true single-row cases.
- Handle no-op or not-found cases explicitly.
- Use the same captured values for both API responses and audit logging when needed.

