Check SQL Server Plan Cache Pollution (I) in 45 Seconds, The "45 Seconds DBA Series" | Part 22

Before we dive into today's topic, if you missed my previous post you can take a look at SQL SERVER “If SQL Server Errors Were Honest” - Sunday SQL Humor 😅. 👉 If you found this deep-dive helpful, feel free to check out the ads—your support helps me keep creating high-quality SQL Server content for the community.

Check SQL Server Plan Cache Pollution (I) in 45 Seconds

The "45 Seconds DBA Series" | Part 22
Abstract high-speed data connection

In this post, I’ll show you how "single-use" execution plans are silently hijacking your server's RAM and how to stop this memory waste in under a minute.

🧠 TL;DR BOX
✔️ Plan Cache Pollution occurs when non-parameterized queries create thousands of useless, single-use execution plans. 💣
✔️ Memory Starvation: These plans steal space from the Buffer Pool, forcing data out of memory and increasing I/O. 📉
✔️ The Fix: Use Parameterization, sp_executesql, or enable the "Optimize for Ad Hoc Workloads" setting. 🚀
✔️ SQL WOW Query: Identify exactly which ad-hoc queries are polluting your cache right now. 🧪

Hi SQL SERVER Guys and Gals,

In 25 years of performance tuning, I’ve seen millions of dollars in hardware rendered useless by poor coding habits. One of the most common silent killers is Plan Cache Pollution. It's the technical equivalent of filling a library with identical books that have only one page changed. Every time your application sends a query like WHERE Id = 1 and then WHERE Id = 2 without parameterization, SQL Server treats them as brand-new logic.

🔍 DIAGNOSIS: What is Killing Your RAM?

The Plan Cache: This is a dedicated area in SQL Server's memory (part of the memory clerk CACHESTORE_SQLCP) designed to store execution plans. Its purpose is to save CPU cycles by reusing plans for frequently executed queries.

💣 Single-Use Plans: When an ORM or a developer concatenates strings to build queries, SQL Server generates a unique Ad-hoc plan for every single literal value.

  • SELECT * FROM Users WHERE Id = 1 → Plan A compiled.
  • SELECT * FROM Users WHERE Id = 2 → Plan B compiled.

These plans stay in memory, but they will never be used again. This leads to Cache Bloat: thousands of 16KB-128KB plans consuming gigabytes of RAM that should be used to cache actual data pages (Buffer Pool).

🧪 QUERY: Identify the Polluters

Run this "WOW" query to see the top 50 ad-hoc, single-use plans currently wasting space in your cache.

-- 🔍 SQL Server Plan Cache Pollution Diagnostic
SELECT TOP 50
    cp.usecounts,
    cp.size_in_bytes / 1024 AS SizeKB,
    st.text
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
WHERE cp.objtype = 'Adhoc'
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC;

Source: sys.dm_exec_cached_plans (Microsoft Learn)

🚀 FIX: Reclaiming Your Buffer Pool

To solve this, we must move from Ad-hoc to Parameterized workloads. Here are your primary weapons:

  • 🧠 Parameterization: Ensure your application uses @Parameter instead of literal values.
  • sp_executesql: Replace EXEC(@sql) with sp_executesql to promote plan reuse.
  • 🔧 Optimize for Ad Hoc Workloads: This server-level setting tells SQL Server to store only a small "plan stub" the first time a query is seen, saving memory until the query is actually reused.
-- 🚀 Enable Optimize for Ad Hoc Workloads
EXEC sys.sp_configure N'show advanced options', N'1';
RECONFIGURE;
EXEC sys.sp_configure N'optimize for ad hoc workloads', N'1';
RECONFIGURE;

🚀 My REAL Strategy

In my experience, Forced Parameterization at the database level can be a double-edged sword. While it aggressively stops pollution, it can trigger massive Parameter Sniffing issues.

My rule of thumb: If your single-use plans exceed 30% of your total cache size, enable "Optimize for Ad Hoc Workloads" first. It’s a low-risk, high-reward move that immediately relieves memory pressure without changing query plan behavior.

📊 TAKEAWAY: Milliseconds Matter

💣 Your buffer pool is starving because useless plans are stealing memory.

Every execution plan that is compiled and never reused is a triple loss: wasted CPU for compilation, wasted Memory for storage, and wasted I/O because data pages were evicted from the buffer pool to make room for that junk. Stop the leak today.

📢 Support the Blog: Did you find this deep-dive helpful? The ads you see here are selected to reflect your interests. If a partner's offer catches your eye, give it a look! Your engagement helps me continue publishing premium SQL Server content for the community.

Biondi Luca @2026 - Sharing over 25 years of Gained Knowledge for Passion. Share if you like my posts!

🧠 Master SQL Server Performance Hub

Expand your knowledge with my latest deep-dives and performance guides.

Don't forget to check my April 2026 Recap for a complete monthly summary.

Comments

I Post più popolari

Speaking to Sql Server, sniffing the TDS protocol

SQL Server, find text in a Trigger, Stored Procedures, View and Function. Two ways and what ways is better

SQL Server, Avoid that damn Table Spool!