Welcome back. In the last lesson you learned safe write patterns for INSERT/UPDATE/DELETE.
Now we move to a tool DBAs use constantly during troubleshooting, reporting, and operational scripting:
temporary storage.
When you diagnose incidents or build repeatable scripts, you often need to:
SQL Server gives you two primary tools for this:
temporary tables and table variables.
They look similar, but they behave differently—especially for performance.
Lesson description: You will learn what temp tables (#temp) and table variables (@table) are,
how they differ in scope, indexing, statistics, and performance, when DBAs choose each one,
safe patterns for incident scripts, and how to avoid common mistakes that lead to slow queries or incorrect results.
tempdb (just like other temp objects).DBA rule: If you are unsure and expect non-trivial row counts, start with a temp table.
It is usually the safer default for real-world DBA scripts.
A local temp table (#Something) exists for your current session until you drop it or disconnect.
It is visible to that session, and to nested procedure calls within that session.
-- Create a temp table
CREATE TABLE #TargetBookings
(
BookingID INT NOT NULL PRIMARY KEY,
Status VARCHAR(20) NOT NULL
);
-- Use it
INSERT INTO #TargetBookings (BookingID, Status)
SELECT BookingID, Status
FROM dbo.Booking
WHERE Status IN ('NEW','PENDING');
-- It remains available in this session until dropped or session ends
SELECT * FROM #TargetBookings;
DROP TABLE #TargetBookings;
A table variable exists only within the batch/procedure where it is declared.
DECLARE @TargetBookings TABLE
(
BookingID INT NOT NULL PRIMARY KEY,
Status VARCHAR(20) NOT NULL
);
INSERT INTO @TargetBookings (BookingID, Status)
SELECT BookingID, Status
FROM dbo.Booking
WHERE Status IN ('NEW','PENDING');
SELECT * FROM @TargetBookings;
Once the batch ends, @TargetBookings is gone.
DBAs care about performance because slow scripts can:
Here is the key performance difference:
This is why temp tables are commonly the “DBA default” for troubleshooting scripts that handle more than a tiny set of rows.
You can create indexes after loading data (useful when you do not know how many rows will be staged).
CREATE TABLE #PaymentsStage
(
PaymentID INT NOT NULL,
BookingID INT NOT NULL,
PaymentStatus VARCHAR(20) NOT NULL,
AmountUSD DECIMAL(10,2) NOT NULL,
PaidAt DATETIME2 NULL
);
INSERT INTO #PaymentsStage (PaymentID, BookingID, PaymentStatus, AmountUSD, PaidAt)
SELECT PaymentID, BookingID, PaymentStatus, AmountUSD, PaidAt
FROM dbo.Payment;
-- Add an index after loading
CREATE INDEX IX_PaymentsStage_BookingID ON #PaymentsStage (BookingID);
-- Now joins/grouping by BookingID become faster
SELECT BookingID, COUNT(*) AS Attempts
FROM #PaymentsStage
GROUP BY BookingID;
Table variables can have a PRIMARY KEY or UNIQUE constraint, which creates an index,
but they are less flexible than temp tables for ad-hoc indexing decisions.
DECLARE @PaymentsStage TABLE
(
PaymentID INT NOT NULL PRIMARY KEY,
BookingID INT NOT NULL,
PaymentStatus VARCHAR(20) NOT NULL,
AmountUSD DECIMAL(10,2) NOT NULL,
PaidAt DATETIME2 NULL
);
INSERT INTO @PaymentsStage (PaymentID, BookingID, PaymentStatus, AmountUSD, PaidAt)
SELECT PaymentID, BookingID, PaymentStatus, AmountUSD, PaidAt
FROM dbo.Payment;
SELECT BookingID, COUNT(*) AS Attempts
FROM @PaymentsStage
GROUP BY BookingID;
DBA guidance: If you need to add multiple indexes based on how you use the staged data,
temp tables are usually the better tool.
DBA reality: Temp tables are used more often in performance-sensitive operational work.
Table variables are used more often for small helper sets.
One of the most professional DBA patterns is:
This reduces the risk of changing rows you did not intend to change.
-- 1) Stage target booking IDs
CREATE TABLE #Target
(
BookingID INT NOT NULL PRIMARY KEY
);
INSERT INTO #Target (BookingID)
SELECT b.BookingID
FROM dbo.Booking AS b
WHERE b.Status = 'NEW'
AND b.PickupAt < DATEADD(HOUR, -24, SYSDATETIME()); -- example condition
-- 2) Review the exact targets (DBA safety step)
SELECT b.BookingID, b.ConfirmationCode, b.Status, b.PickupAt
FROM dbo.Booking AS b
JOIN #Target AS t
ON t.BookingID = b.BookingID
ORDER BY b.PickupAt ASC;
-- 3) Apply change using join to target list (prevents accidental wide updates)
BEGIN TRAN;
UPDATE b
SET b.Status = 'EXPIRED'
FROM dbo.Booking AS b
JOIN #Target AS t
ON t.BookingID = b.BookingID;
-- Validate impact
SELECT b.BookingID, b.Status
FROM dbo.Booking AS b
JOIN #Target AS t
ON t.BookingID = b.BookingID;
-- COMMIT TRAN;
-- ROLLBACK TRAN;
DROP TABLE #Target;
DBA note: This is safer than writing a complex WHERE clause directly in the UPDATE.
Both temp tables and table variables use tempdb resources in SQL Server.
In real production systems, tempdb is one of the most common bottlenecks (especially under heavy workloads).
DBAs therefore follow these practical rules:
This often causes slow plans due to row estimation issues. If you might have a lot of rows, use a temp table.
If you rerun scripts in the same session, you can hit “object already exists” errors.
DBAs often include a safe drop check:
IF OBJECT_ID('tempdb..#Target') IS NOT NULL
DROP TABLE #Target;
SELECT INTO can be convenient, but DBAs prefer explicit CREATE TABLE for predictable types and constraints.
SELECT INTO can produce unexpected types/lengths.
Do these exercises in your lab:
#TargetBookings temp table containing BookingID for all bookings with Status = ‘NEW’.#PaymentsStage and load payments, then create an index on BookingID and run a GROUP BY summary.Next, you will apply everything from Section 3 in a realistic environment using a sample database.
You will write SELECT queries, joins, aggregates, safe filters, and staging patterns to create practical DBA-style reports.
Not a member yet? Register now
Are you a member? Login now