Query Tuning
Breaking complex logic with CTEs and temp tables
Stage intermediate results so the optimizer can estimate each step honestly.
/*
================================================================================
Purpose: Analyzes TempDB file allocations (file counts, uniformity of
sizes/growths) and identifies PAGELATCH allocation bottlenecks.
Provides: TempDB file counts, growth uniformity, active PAGELATCH
contention.
Importance: TempDB is a shared resource; contention here bottlenecks the
entire instance.
Interpretation: Files should match cores (up to 8). Mismatched growth is
critical. PAGELATCH waits indicate contention.
Action: If file count < @ExpectedFiles, add equal-sized data files (1 per core/CPU up to 8). If file sizes are not uniform, resize all files to the same size. If PAGELATCH waits are detected, adding files often resolves allocation contention. Restart is NOT required after adding TempDB files.
Criticality: High
Author: Ravi Sharma
================================================================================
*/
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET NOCOUNT ON;
-- 1. TempDB File Configuration Analysis
PRINT '--- TempDB File Configuration & Uniformity ---';
DECLARE @LogCores INT = (SELECT cpu_count FROM sys.dm_os_sys_info);
DECLARE @ExpectedFiles INT = CASE WHEN @LogCores <= 8 THEN @LogCores ELSE 8 END;
SELECT
name AS [Logical_File_Name],
type_desc AS [File_Type],
size * 8 / 1024 AS [Size_MB],
growth AS [Growth_Value],
is_percent_growth AS [Is_Percent_Growth],
CASE
WHEN is_percent_growth = 1 THEN '🔴 CRITICAL: Use fixed-size growth (e.g. 256MB) to ensure uniformity.'
WHEN growth <> (SELECT TOP 1 growth FROM sys.master_files WHERE database_id = 2 AND type = 0 ORDER BY file_id)
THEN '🔴 CRITICAL: Mismatched growth increments will break proportional fill.'
ELSE '🟢 OPTIMAL'
END AS [Growth_Uniformity_Status],
(SELECT COUNT(*) FROM sys.master_files WHERE database_id = 2 AND type = 0) AS [Data_Files_Count],
@ExpectedFiles AS [Target_Data_Files],
CASE
WHEN (SELECT COUNT(*) FROM sys.master_files WHERE database_id = 2 AND type = 0) < @ExpectedFiles
THEN '🟡 WARNING: File count should match logical cores up to 8.'
ELSE '🟢 OPTIMAL'
END AS [File_Count_Status]
FROM sys.master_files WITH (NOLOCK)
WHERE database_id = 2; -- TempDB database_id is 2
-- 2. Detect TempDB PAGELATCH Contention (PFS, GAM, SGAM)
PRINT '--- Active TempDB PAGELATCH Contention (Allocation Pages) ---';
SELECT
session_id,
wait_type,
wait_duration_ms,
resource_description,
CASE
WHEN resource_description LIKE '2:%:1' OR resource_description LIKE '2:%:125440' THEN '🔴 CRITICAL: PFS page contention. More files or uniform growth needed.'
WHEN resource_description LIKE '2:%:2' THEN '🔴 CRITICAL: GAM page contention.'
WHEN resource_description LIKE '2:%:3' THEN '🔴 CRITICAL: SGAM page contention.'
ELSE '⚪ INFO: Other Page Contention'
END AS [Expert_Guidance]
FROM sys.dm_os_waiting_tasks WITH (NOLOCK)
WHERE wait_type LIKE 'PAGELATCH_%'
AND resource_description LIKE '2:%'; -- Database 2 is TempDB
-- 3. Historical Allocation Contention (Waits)
PRINT '--- Historical Allocation Contention (Waits) ---';
SELECT
wait_type,
waiting_tasks_count,
wait_time_ms,
CASE
WHEN wait_type IN ('PAGELATCH_UP', 'PAGELATCH_EX', 'PAGELATCH_SH') THEN '🟡 WARNING: Potential TempDB Metadata/Allocation bottleneck'
ELSE '🟢 NORMAL'
END AS [Wait_Status]
FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'PAGELATCH_%'
AND waiting_tasks_count > 0
ORDER BY wait_time_ms DESC;
Review the full script and confirm the connected SQL Server instance before execution. The run instructions and output guide appear later in this article.
Dummy guide: nested views and mega-queries hide bad estimates.
Rewrite the monster into named CTE stages for humans, then materialize expensive stages into
#temp tables when the optimizer keeps underestimating. Index the temp when the next step
filters or joins on it more than once.
Do not assume every CTE is materialized — in SQL Server it usually is not. Measure with actual plans, not folklore.
Why intermediate result design deserves a method
The central principle is that CTEs improve expression but do not automatically materialize or create statistics. A single metric, recommendation, or successful command is not enough. The decision has to connect an observed symptom to a plausible cause, a controlled change, and a result that can be verified under the workload that matters.
Evidence to collect
Use evidence from the same time window whenever possible:
- estimated versus actual rows
- repeated references to the intermediate result
- tempdb cost and indexing opportunity
- compile behavior and parameter skew
A practical workflow
- Start with the simplest correct query.
- Inspect the actual plan and estimates.
- Test a temporary-table breakpoint when estimates diverge.
- Compare reads, CPU, duration, and concurrency.
Common mistakes
Avoid these shortcuts:
- assuming every CTE is materialized
- replacing all CTEs with temp tables
- testing only one parameter value
Verification and completion
Define completion before making the change. Confirm correctness first, then compare latency, throughput, reads, CPU, waits, storage behavior, or recovery time as appropriate for Breaking complex logic with CTEs and temp tables. Test negative and boundary cases, not only the path expected to succeed. Watch the system long enough to cover the relevant workload pattern.
Download and run the diagnostic
TempDB configuration diagnostic
Download tempdb_configuration.sqlBefore you run it
- Review every statement and confirm the connection points to the intended SQL Server database server.
- Run through a non-production or read-only diagnostic account first when possible.
- Required access: VIEW SERVER STATE.
- Save the output with the server name, database name, collection time, and workload window so another engineer can reproduce the analysis.
What it does
Reports TempDB files, size, growth, placement, and configuration signals relevant when temporary objects or spills become part of a rewrite. The script is diagnostic and does not apply an automatic remediation. Result values still need workload context, a second supporting signal, and a before-and-after comparison.
How to read the output values
Compare file sizes and growth increments. Uneven sizes can undermine proportional fill. Small growth increments can cause repeated pauses. This output describes TempDB capacity; use an actual plan and task-space DMVs to prove that a CTE or temporary-table choice drives the load.