Table Variables in SQL Server: The Rollback Behavior That Actually Matters

Written by

in

Table Variables in SQL Server: The Rollback Behavior That Actually Matters

Table variables look like a smaller, simpler version of a temp table. The real difference is behavioral, not just syntactic — and it’s specific enough to be one of the most commonly asked SQL Server interview questions, covered again in Chapter 11.

Same ROLLBACK, different fate(table variable vs #temp table, one transaction)@Log (table variable)INSERT INTO @Log VALUES(…)inside BEGIN TRAN#Log (#temp table)INSERT INTO #Log VALUES(…)inside the SAME BEGIN TRANROLLBACK;row still theretable var survives the rollbackEMPTY#temp table’s DML rolled back tooWhy? A table var isn’tlogged like #temp DML —SQL Server treats it morelike an ordinary variable. 📌

Using One

DECLARE @DriverTotals TABLE (
    driver_name NVARCHAR(100),
    total_fare  DECIMAL(10,2)
);

INSERT INTO @DriverTotals
SELECT driver_name, SUM(fare_usd)
FROM dbo.TripAdvanced
GROUP BY driver_name;

SELECT * FROM @DriverTotals ORDER BY total_fare DESC;

The Behavior That Actually Surprises People in Production

DECLARE @Log TABLE (msg NVARCHAR(200));
CREATE TABLE #Log (msg NVARCHAR(200));

BEGIN TRAN;
    INSERT INTO @Log VALUES ('table variable row');
    INSERT INTO #Log VALUES ('temp table row');
ROLLBACK;

SELECT * FROM @Log;  -- still has the row!
SELECT * FROM #Log;  -- empty — rolled back
DROP TABLE #Log;

If you’re logging errors into a table variable inside a transaction that then fails and rolls back, the table variable’s contents survive — that’s often exactly the behavior you want for an error log, and exactly why table variables exist as a distinct tool, not just “a smaller #temp table.” The underlying reason: a table variable is not itself part of the surrounding transaction’s log the same way a #temp table’s DML operations are — SQL Server treats it more like an ordinary local variable for durability purposes, even though it physically also lives in tempdb.

-- The realistic use case: an error log that survives the very rollback it's reporting on
BEGIN TRY
    BEGIN TRAN;
        UPDATE dbo.Employee SET salary = salary * 1.1;
        INSERT INTO @Log VALUES ('About to check a business rule...');
        IF EXISTS (SELECT 1 FROM dbo.Employee WHERE salary > 1000000)
            THROW 51000, 'Salary cap exceeded', 1;
    COMMIT;
END TRY
BEGIN CATCH
    ROLLBACK;
    INSERT INTO @Log VALUES ('Rolled back: ' + ERROR_MESSAGE());
END CATCH;
SELECT * FROM @Log; -- both log entries are here, even though the UPDATE itself was undone

Quick Comparison

#temp table @table variable
Scope Session (and nested calls) Batch or procedure only
Statistics Real, maintained Historically none (improved somewhat in 2019+ with deferred compilation, but still generally weaker)
Transaction rollback Rolled back Survives
Can be altered after creation Yes (ADD COLUMN, CREATE INDEX) No — structure is fixed at DECLARE time

That last row matters in practice too: if you need to add an index to your intermediate result after seeing what the data looks like, or need to ALTER its structure partway through a script, a table variable can’t do that — its full structure must be declared upfront.

Practice tip: Rebuild the TRY/CATCH error-logging example above yourself, and swap @Log for a #Log temp table to see the log entries vanish along with the rollback. That side-by-side comparison, run yourself, is the fastest way to make this genuinely memorable rather than a fact you memorized for an interview.

Enjoyed this?

Subscribe to get every new SQL Server lesson as soon as it’s published, and share it with a developer who’d find it useful.

📡 Subscribe via RSS  | 
Share on X  | 
Share on LinkedIn  | 
Share on Facebook

Want the full structured course with quizzes, projects, and 10+ exercises per chapter? Check out SQL Server for Developers & DBAs, coming soon on this site.