<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Performance Troubleshooting | CraftedSQL</title>
	<atom:link href="https://www.craftedsql.com/tag/performance-troubleshooting/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Tailored SQL Solutions</description>
	<lastBuildDate>Sat, 26 Sep 2026 14:54:58 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://www.craftedsql.com/wp-content/uploads/2024/11/CraftedSQL-Website-Icon-150x150.png</url>
	<title>Performance Troubleshooting | CraftedSQL</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Server Execution Plans Need Runtime Context</title>
		<link>https://www.craftedsql.com/sql-server-execution-plans-runtime-context/</link>
		
		<dc:creator><![CDATA[Bjoern@CraftedSQL]]></dc:creator>
		<pubDate>Thu, 24 Sep 2026 10:37:00 +0000</pubDate>
				<category><![CDATA[Performance & Optimization]]></category>
		<category><![CDATA[Cardinality Estimation]]></category>
		<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[Parameter Sensitivity]]></category>
		<category><![CDATA[Performance Troubleshooting]]></category>
		<category><![CDATA[Query Performance]]></category>
		<category><![CDATA[Query Store]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://www.craftedsql.com/?p=21189</guid>

					<description><![CDATA[<p>SQL Server execution plans are often the first place I look when a query behaves differently from what I would expect. They tell me how SQL Server chose to access the data, which join strategies it selected and where estimates may not match what actually happened during execution. An actual plan can add runtime information ... <a title="SQL Server Execution Plans Need Runtime Context" class="read-more" href="https://www.craftedsql.com/sql-server-execution-plans-runtime-context/" aria-label="Read more about SQL Server Execution Plans Need Runtime Context">Read more</a></p>
<p>The post <a href="https://www.craftedsql.com/sql-server-execution-plans-runtime-context/">SQL Server Execution Plans Need Runtime Context</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">SQL Server execution plans are often the first place I look when a query behaves differently from what I would expect. They tell me how SQL Server chose to access the data, which join strategies it selected and where estimates may not match what actually happened during execution. An actual plan can add runtime information as well, so there is usually a lot to work with.</p>



<p class="wp-block-paragraph">The problem starts when people treat the plan as if it were the complete explanation for a performance incident.</p>



<p class="wp-block-paragraph">Suppose users report that an application was slow around 10:00. Someone investigates an hour later, runs the query again and captures an actual execution plan. The query is fast now. The plan looks reasonable. There may still be useful clues in it, but we are already looking at a different execution under different conditions. If I want to understand what happened at 10:00, the plan captured at 11:00 is only one part of the evidence.</p>



<p class="wp-block-paragraph">That distinction sounds obvious, but it matters because execution plans are very persuasive. They are visual, detailed and concrete. It is easy to find an operator that looks suspicious and start tuning from there, even when we have not yet established whether that operator had anything to do with the problem users experienced.</p>



<h2 class="wp-block-heading">What SQL Server execution plans can tell you</h2>



<p class="wp-block-paragraph">An execution plan describes the strategy SQL Server selected for a statement. The optimizer makes that decision using the information available at compile time: statistics, indexes, cardinality estimates, parameter values and other conditions that can influence the plan.</p>



<p class="wp-block-paragraph">An estimated plan shows that compiled strategy without running the query. An actual execution plan adds information from the execution itself, including actual row counts and, depending on the plan and SQL Server version, other runtime details. Microsoft explains the distinction in its documentation on <a href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/display-and-save-execution-plans">displaying and saving execution plans</a>.</p>



<p class="wp-block-paragraph">For troubleshooting, I usually prefer the actual plan because I can compare what SQL Server expected with what it really processed. If SQL Server expected a few rows but processed several hundred thousand instead, that is worth investigating. The mismatch may affect join choices, memory requirements or the amount of work performed further up the plan.</p>



<p class="wp-block-paragraph">But the difference itself is not yet a diagnosis. I still need to know whether it helps explain the slow execution I am investigating.</p>



<h3 class="wp-block-heading">Parameter-sensitive workloads change the picture</h3>



<p class="wp-block-paragraph">This becomes especially relevant with parameter-sensitive workloads. A stored procedure may use the same cached plan for one parameter value that returns ten rows and another that returns several hundred thousand. The plan shape can remain almost unchanged while the actual workload is very different. Data distribution can make that difference larger, and so can changes in the surrounding workload. A plan that works perfectly well for one execution can be much less suitable for another without looking obviously broken when viewed in isolation.</p>



<p class="wp-block-paragraph">The same caution applies in the other direction. A scan, a warning or a high estimated cost may look uncomfortable, but none of those things automatically explains the incident. A scan against a small table may be entirely appropriate. An expensive operation that runs once at night may matter less than a small query executed thousands of times during business hours.</p>



<p class="wp-block-paragraph">What I am trying to establish is not whether the plan contains something that could be improved. On a sufficiently busy SQL Server, I can usually find something. I want to know whether the plan explains the behavior that actually caused the problem.</p>



<h2 class="wp-block-heading">The surrounding workload changes what the plan means</h2>



<p class="wp-block-paragraph">A query can use the same execution plan and still take very different amounts of time.</p>



<p class="wp-block-paragraph">Another transaction may block it. Storage latency may increase. CPU pressure can come from unrelated activity, while several normally harmless queries may start running at the same time and compete for the same resources. None of that necessarily changes the plan shape.</p>



<p class="wp-block-paragraph">This is one reason I do not like looking at SQL Server execution plans completely on their own during a production incident. The plan shows what SQL Server intended to do with the query, but elapsed time also includes waiting. If the query spent most of that time waiting for a lock or for storage, tuning an operator may solve the wrong problem.</p>



<p class="wp-block-paragraph">Wait statistics can help here because they add another view of what SQL Server was waiting for. They have the same limitation, though: they are evidence, not a diagnosis on their own. I wrote about that in <a href="https://www.craftedsql.com/sql-server-wait-statistics-context/">SQL Server Wait Statistics Are Not a Diagnosis</a>.</p>



<p class="wp-block-paragraph">This is also where the timing of the investigation becomes important. If I have an actual plan from the affected execution, together with runtime metrics and information about blocking or resource pressure at the same time, the pieces can start to support each other. If I only have a plan captured later, I have to be much more careful about what I conclude from it.</p>



<p class="wp-block-paragraph">That is often the difference between finding a plausible tuning opportunity and actually explaining an incident.</p>



<h2 class="wp-block-heading">When SQL Server execution plans need history</h2>



<p class="wp-block-paragraph">Teams often investigate SQL Server performance problems after the interesting part has already happened.</p>



<p class="wp-block-paragraph">The application was slow earlier in the morning. Users complained, perhaps monitoring raised an alert, and by the time someone starts looking closely everything appears normal again. CPU is back at its usual level, the blocking chain has disappeared and the query that supposedly caused trouble now finishes quickly.</p>



<p class="wp-block-paragraph">At that point, current SQL Server execution plans can tell me how the query behaves now. What I really need is a way to compare the current behavior with the period where the problem existed.</p>



<h3 class="wp-block-heading">Looking back with Query Store</h3>



<p class="wp-block-paragraph">Query Store helps with exactly that. It keeps query texts, execution plans and aggregated runtime statistics over time, which makes it possible to look back instead of relying only on what I can reproduce at the moment of investigation. Microsoft describes the collection process in <a href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/how-query-store-collects-data?view=sql-server-ver16">How Query Store Collects Data</a>.</p>



<p class="wp-block-paragraph">I may find that the query changed plans shortly before duration increased. That does not prove the new plan caused the regression, but it gives me something concrete to investigate. I can compare both plans, look at the affected runtime intervals and see whether the timing fits.</p>



<p class="wp-block-paragraph">Sometimes the more interesting finding is that the plan did not change at all.</p>



<p class="wp-block-paragraph">If Query Store shows the same plan before, during and after the incident, forcing a different plan becomes much harder to justify. I would start looking more closely at the parameter values used during the slow period, concurrency, blocking or resource pressure. The unchanged plan does not tell me what the cause was, but it removes one attractive explanation from the top of the list.</p>



<p class="wp-block-paragraph">That historical perspective is the reason I consider Query Store so useful in production troubleshooting. I covered that in more detail in <a href="https://www.craftedsql.com/sql-server-query-store-performance-history/">SQL Server Query Store: Why Performance History Matters</a>.</p>



<p class="wp-block-paragraph">Without history, it is very easy to compare a bad user experience from 10:00 with a perfectly healthy execution from 11:00 and assume both represent the same situation.</p>



<p class="wp-block-paragraph">They may not.</p>



<h2 class="wp-block-heading">Before I change production, I want the story to make sense</h2>



<p class="wp-block-paragraph">A performance investigation eventually reaches the point where someone wants to change something. Maybe an index is missing. Maybe a different plan would work better. Perhaps a query needs to be rewritten, or a temporary mitigation is required because the application is already affected.</p>



<p class="wp-block-paragraph">During an active incident, restoring service can take priority. There are situations where I will make a reversible operational change before I understand every detail of the root cause. That is different from deciding what the permanent fix should be.</p>



<p class="wp-block-paragraph">Permanent tuning changes have consequences. An index helps reads but also affects writes and consumes storage. A hint limits the optimizer&#8217;s choices. A forced plan can stabilize a regression and later become a problem itself when the data or workload changes.</p>



<p class="wp-block-paragraph">Before I make that kind of change, I want the evidence to fit together reasonably well. The plan should match the execution I am trying to explain, and the runtime behavior should support the hypothesis. Timing matters too. If historical data is available, I also want to know whether the behavior was unusual or simply normal workload seen at an inconvenient moment.</p>



<p class="wp-block-paragraph">That does not require perfect certainty. Production troubleshooting rarely gives us that.</p>



<p class="wp-block-paragraph">It does require enough context that I can explain why I expect the change to address the actual problem rather than merely improve something that looked suspicious in the plan.</p>



<p class="wp-block-paragraph">For me, that is where SQL Server execution plans are most useful. They narrow the investigation, show me where SQL Server&#8217;s decisions deserve a closer look and give me something concrete to compare against runtime behavior and history.</p>



<p class="wp-block-paragraph">They are strong evidence, but rarely the whole story.</p>



<p class="has-small-font-size wp-block-paragraph">Foto von <a href="https://unsplash.com/de/@davidkristianto?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">David Kristianto</a> auf <a href="https://unsplash.com/de/fotos/luftaufnahme-eines-stark-befahrenen-autobahnkreuzes-in-der-abenddammerung-zzM7voOp6Tk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>The post <a href="https://www.craftedsql.com/sql-server-execution-plans-runtime-context/">SQL Server Execution Plans Need Runtime Context</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SQL Server Query Store: Why Performance History Matters</title>
		<link>https://www.craftedsql.com/sql-server-query-store-performance-history/</link>
					<comments>https://www.craftedsql.com/sql-server-query-store-performance-history/#comments</comments>
		
		<dc:creator><![CDATA[Bjoern@CraftedSQL]]></dc:creator>
		<pubDate>Thu, 06 Aug 2026 11:12:00 +0000</pubDate>
				<category><![CDATA[Performance & Optimization]]></category>
		<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[Performance Troubleshooting]]></category>
		<category><![CDATA[Query Store]]></category>
		<category><![CDATA[SQL Server Performance]]></category>
		<guid isPermaLink="false">https://www.craftedsql.com/?p=21056</guid>

					<description><![CDATA[<p>A SQL Server performance problem is not always still there when someone finally has time to investigate it. SQL Server Query Store becomes especially useful when the system has already returned to normal. Users may report that an application was slow around 10:00. An hour later, response times are back to normal. CPU usage looks ... <a title="SQL Server Query Store: Why Performance History Matters" class="read-more" href="https://www.craftedsql.com/sql-server-query-store-performance-history/" aria-label="Read more about SQL Server Query Store: Why Performance History Matters">Read more</a></p>
<p>The post <a href="https://www.craftedsql.com/sql-server-query-store-performance-history/">SQL Server Query Store: Why Performance History Matters</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">A SQL Server performance problem is not always still there when someone finally has time to investigate it. SQL Server Query Store becomes especially useful when the system has already returned to normal.</p>



<p class="wp-block-paragraph">Users may report that an application was slow around 10:00. An hour later, response times are back to normal. CPU usage looks ordinary and there is no obvious blocking. The query mentioned by the application team now finishes without anything particularly suspicious in its execution plan.</p>



<p class="wp-block-paragraph">That is a common difficulty in performance troubleshooting: the system you are looking at now may no longer be the system users experienced during the incident.</p>



<p class="wp-block-paragraph">At that point, a current execution plan or a snapshot of server activity can still be useful, but it cannot tell the whole story. I want to know what happened around 10:00, not only what SQL Server looks like at 11:00.</p>



<p class="wp-block-paragraph">Query Store keeps <a href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store?view=sql-server-ver17" target="_blank" rel="noopener">historical information about queries, execution plans and runtime behavior</a>. That means an investigation does not have to rely entirely on the state of the database after the problem has disappeared.</p>



<h2 class="wp-block-heading">Start with when the problem happened</h2>



<p class="wp-block-paragraph">When I have a reasonably clear incident window, that is usually where I start.</p>



<p class="wp-block-paragraph">If the application team tells me that response times became poor between 09:45 and 10:15, I start with that period. I can then compare the affected queries with their behavior before or afterwards.</p>



<p class="wp-block-paragraph">I am not immediately looking for the most expensive query in the database.</p>



<p class="wp-block-paragraph">A query that consumes a lot of CPU every day may be completely unrelated to the incident. A query that normally gets little attention can suddenly become interesting. Its duration or execution pattern may have changed during exactly the period users reported a problem.</p>



<p class="wp-block-paragraph">The first comparison is therefore fairly simple.</p>



<p class="wp-block-paragraph">Did the affected query actually become slower around that time? Was another execution plan used? Did the query execute much more often than usual? And does the same behavior appear at other times as well?</p>



<p class="wp-block-paragraph">Those answers do not give me a diagnosis yet, but they help narrow the investigation.</p>



<h2 class="wp-block-heading">A different execution plan is a clue</h2>



<p class="wp-block-paragraph">One of the things Query Store makes easy to see is whether a query has used different execution plans over time.</p>



<p class="wp-block-paragraph">If a query usually runs with one plan and performed much worse while another plan was active, that deserves attention. It still does not mean that the second plan explains everything that happened.</p>



<p class="wp-block-paragraph">The workload may have been different. Parameter values may have changed. Data distribution may have influenced the optimizer. There may also have been resource pressure elsewhere on the server at the same time.</p>



<p class="wp-block-paragraph">This is why I am careful with the jump from “different plan” to “bad plan”.</p>



<p class="wp-block-paragraph">Sometimes the plan really is the immediate problem, and forcing a previously known plan can be a reasonable way to stabilize an important workload. During an incident, restoring acceptable service may be more important than understanding every detail immediately.</p>



<p class="wp-block-paragraph">Afterwards, however, I still want to know why the behavior changed in the first place.</p>



<p class="wp-block-paragraph">Otherwise, I have fixed the symptom without learning much from the incident.</p>



<h2 class="wp-block-heading">SQL Server Query Store puts execution plans into context</h2>



<p class="wp-block-paragraph">The execution plan is only one part of the picture.</p>



<p class="wp-block-paragraph">What I find more useful during a retrospective investigation is comparing the query across different periods. If duration increased sharply for half an hour and then returned to normal, I have a much narrower problem to investigate.</p>



<p class="wp-block-paragraph">The same applies when the pattern repeats.</p>



<p class="wp-block-paragraph">Perhaps the query becomes slower every morning around the same time. That immediately raises different questions. Is there a batch process running? Does the application generate a different workload during that period? Is data being imported? Is maintenance running somewhere in the environment?</p>



<p class="wp-block-paragraph">This becomes particularly relevant with ETL and analytical workloads, where growing processing windows can gradually begin to overlap with reporting or other activity. I look at that scenario in more detail in <a href="/sql-server-data-warehouse-performance/">SQL Server performance for data warehouse workloads</a>.</p>



<p class="wp-block-paragraph">The same pattern can appear across broader <a href="/sql-server-business-intelligence-performance/">SQL Server Business Intelligence workloads</a>, where ETL, semantic model processing and reporting may depend on the same database platform at different times of the day. Power BI is one specific example: a report or DirectQuery workload may perform normally most of the day but become slow when query patterns, concurrency or resource pressure change on the SQL Server side. The <a href="/sql-server-performance-power-bi/">SQL Server performance for Power BI workloads</a> page looks at that relationship in more detail.</p>



<p class="wp-block-paragraph">Query Store will not answer all of those questions.</p>



<p class="wp-block-paragraph">What it can do is show that the change happened and help define the period I need to investigate elsewhere.</p>



<p class="wp-block-paragraph">That becomes particularly useful when I compare the Query Store history with monitoring data from SQL Server, the operating system, storage or the virtualization platform. If several independent sources show a change during the same period, the investigation becomes much more focused.</p>



<p class="wp-block-paragraph">This also prevents one of the easier mistakes in troubleshooting: finding an interesting SQL Server observation and trying to make the entire incident fit around it. I discussed that problem in more detail in <a href="https://www.craftedsql.com/sql-server-bottleneck-triage-signals-causes/">SQL Server Bottleneck Triage: How to Separate Signals from Causes</a>.</p>



<h2 class="wp-block-heading">Historical data has to exist before you need it</h2>



<p class="wp-block-paragraph">There is a practical operational consequence to all of this.</p>



<p class="wp-block-paragraph">If Query Store is supposed to help with incidents that happened yesterday or during a short period overnight, it needs to retain useful data for long enough to make that possible.</p>



<p class="wp-block-paragraph">That sounds obvious, but it is something I would rather decide while the system is healthy than discover during an investigation.</p>



<p class="wp-block-paragraph">Capture settings, storage limits, retention and the workload itself influence how useful the available history will be. There is no single configuration that fits every database. This should therefore be an intentional operational decision rather than something left untouched indefinitely.</p>



<p class="wp-block-paragraph">For an important workload, I want to know roughly how far back I can investigate. I also want to know whether the data being collected is useful for the type of incidents that matter to that system.</p>



<p class="wp-block-paragraph">SQL Server Query Store does not replace monitoring or the rest of SQL Server troubleshooting. It gives me another perspective that becomes particularly useful once the immediate symptoms have disappeared.</p>



<p class="wp-block-paragraph">When someone tells me that the application was slow an hour ago, the first question is no longer only what SQL Server is doing now.</p>



<p class="wp-block-paragraph">I want to understand what was different when users actually noticed the problem.</p>
<p>The post <a href="https://www.craftedsql.com/sql-server-query-store-performance-history/">SQL Server Query Store: Why Performance History Matters</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.craftedsql.com/sql-server-query-store-performance-history/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>High SQL Server CPU Is a Signal, Not a Root Cause</title>
		<link>https://www.craftedsql.com/high-sql-server-cpu/</link>
		
		<dc:creator><![CDATA[Bjoern@CraftedSQL]]></dc:creator>
		<pubDate>Wed, 15 Jul 2026 09:23:36 +0000</pubDate>
				<category><![CDATA[Performance & Optimization]]></category>
		<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[Performance Troubleshooting]]></category>
		<category><![CDATA[Root Cause Analysis]]></category>
		<category><![CDATA[SQL Server CPU]]></category>
		<category><![CDATA[SQL Server Performance]]></category>
		<guid isPermaLink="false">https://www.craftedsql.com/?p=21035</guid>

					<description><![CDATA[<p>High CPU usage is easy to see, but the percentage alone does not explain the cause. A useful diagnosis connects workload, execution plans, SQL Server signals and infrastructure data before changes are made.</p>
<p>The post <a href="https://www.craftedsql.com/high-sql-server-cpu/">High SQL Server CPU Is a Signal, Not a Root Cause</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">When CPU usage on a SQL Server host reaches 90 or 95 percent, it is difficult to ignore. Applications may respond slowly, users begin to report delays, and monitoring systems raise alerts. The immediate conclusion often sounds reasonable: the server needs more CPU.</p>



<p class="wp-block-paragraph">That may eventually be the right decision, but it should not be the first one.</p>



<p class="wp-block-paragraph">High CPU usage tells us that processing capacity is under pressure. It does not explain what created that pressure, whether SQL Server is responsible for all of it, or whether additional hardware would address the underlying problem.</p>



<p class="wp-block-paragraph">The percentage is a useful starting point for the investigation. It is not the diagnosis.</p>




<h2 class="wp-block-heading">The same percentage can describe very different situations</h2>



<p class="wp-block-paragraph">A single CPU value has little meaning without timing and workload context.</p>



<p class="wp-block-paragraph">A short increase during a known batch process may be perfectly normal. Sustained CPU pressure during business hours is a different situation. So is a workload that reaches high CPU every morning, after each deployment, or only when a specific report runs.</p>



<p class="wp-block-paragraph">This is why a slow Power BI report and high SQL Server CPU should be investigated as parts of the same workload path rather than as separate symptoms. The <a href="/sql-server-performance-power-bi/">SQL Server performance for Power BI workloads</a> page looks at that scenario in more detail.</p>



<p class="wp-block-paragraph">The same applies to ETL and analytical workloads. High CPU during a growing processing window may reflect legitimate work, inefficient execution or increasing overlap with reporting activity. The <a href="/sql-server-data-warehouse-performance/">SQL Server performance for data warehouse workloads</a> page looks at those interactions in more detail.</p>



<p class="wp-block-paragraph">Two SQL Server systems can show the same CPU utilisation while experiencing completely different problems. One may be processing a legitimate increase in business activity. Another may be using an inefficient execution plan. A third may be competing for physical CPU time with other virtual machines on the same host.</p>



<p class="wp-block-paragraph">The metric looks similar in all three cases, but the cause and the appropriate response are not.</p>



<p class="wp-block-paragraph">Overall CPU utilisation can also hide uneven work distribution across processors or SQL Server schedulers. The headline percentage therefore cannot describe the full operating condition on its own.</p>




<h2 class="wp-block-heading">High SQL Server CPU does not automatically mean insufficient hardware</h2>



<p class="wp-block-paragraph">Additional CPU capacity can reduce immediate pressure. It can also hide an inefficient workload, a regressed execution plan or a configuration problem for a while.</p>



<p class="wp-block-paragraph">If one query consumes most of the available CPU, adding processors may reduce its impact on other users. The query itself can still remain inefficient. If a plan regression caused the increase, the same symptoms may simply return as the workload grows.</p>



<p class="wp-block-paragraph">The infrastructure may also provide less effective processing capacity than its configuration suggests. A virtual machine can have several virtual CPUs assigned while receiving inconsistent access to physical CPU time because of host contention.</p>



<p class="wp-block-paragraph">In that situation, changing indexes or SQL Server settings may not address the actual constraint. Hardware capacity belongs in the investigation, but additional capacity should follow the diagnosis rather than replace it.</p>




<h2 class="wp-block-heading">Start with the timeline</h2>



<p class="wp-block-paragraph">The most useful first question is often not how high CPU usage became, but what changed before the pressure appeared.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">What changed before the CPU pressure appeared?</p>
</blockquote>



<p class="wp-block-paragraph">The answer may point to a deployment, a different execution plan, additional users, a larger data volume, a reporting process running at a new time, or a change in the virtual infrastructure.</p>



<p class="wp-block-paragraph">Sometimes there is no distinct change at all. A system may have operated close to its capacity limit for months, with normal workload growth eventually removing the remaining headroom.</p>



<p class="wp-block-paragraph">The CPU alert may also coincide with a separate issue without being the primary cause of the user-facing slowdown. Building a timeline helps distinguish these situations before teams begin changing queries, configuration or infrastructure.</p>




<h2 class="wp-block-heading">Connect CPU pressure to the workload and execution plans</h2>



<p class="wp-block-paragraph">The next step is to identify which workload was active while the CPU pressure occurred.</p>



<ul class="wp-block-list">

<li>Which queries consumed the most CPU during the affected period?</li>



<li>Did the number of executions increase?</li>



<li>Did execution plans change?</li>



<li>Was parallel execution involved?</li>



<li>Did the workload differ from a normal business day?</li>



<li>Were processes outside SQL Server consuming significant CPU?</li>

</ul>



<p class="wp-block-paragraph">The objective is not to collect every available metric. It is to connect the alert to a workload that can plausibly explain it.</p>



<p class="wp-block-paragraph">A query that looks expensive in isolation may be acceptable if it runs once during an overnight process. A moderately expensive query can become the real problem when an application starts executing it thousands of times per hour.</p>



<p class="wp-block-paragraph">Frequency, timing and business context matter just as much as the cost of one execution.</p>



<p class="wp-block-paragraph">Execution plans deserve particular attention when CPU usage rises without a corresponding increase in business workload. A changed plan can lead to more reads, less efficient join strategies, additional sorting, excessive parallel execution or repeated scans of large tables.</p>



<p class="wp-block-paragraph">Statistics changes, parameter sensitivity, index changes and data growth can all affect plan selection. The important distinction is whether a query has always been expensive or became expensive at a particular point in time.</p>



<p class="wp-block-paragraph">A long-standing design issue may require query or index changes. A sudden regression may require plan analysis, a statistics review or a controlled rollback. Adding CPU may reduce the immediate symptoms in either case, but it still does not explain why the behaviour changed.</p>




<h2 class="wp-block-heading">Compare SQL Server signals with infrastructure data</h2>



<p class="wp-block-paragraph">SQL Server data alone may not fully explain a CPU problem.</p>



<p class="wp-block-paragraph">Wait statistics can add context, but they should not be treated as a standalone verdict. A CPU-intensive workload may show scheduler pressure, runnable tasks waiting for processor time, or waits associated with parallel execution. Storage and memory constraints can also influence query behaviour and increase CPU consumption indirectly.</p>



<p class="wp-block-paragraph">The presence of one wait type does not prove the root cause. The useful question is whether waits, query activity, execution plans and infrastructure measurements describe the same event.</p>



<p class="wp-block-paragraph">Microsoft also recommends correlating SQL Server workload data with operating system and query-level information when investigating high CPU usage. See <a href="https://learn.microsoft.com/en-us/troubleshoot/sql/database-engine/performance/troubleshoot-high-cpu-usage-issues" target="_blank" rel="noopener">Troubleshoot high CPU usage issues in SQL Server</a> for the corresponding technical guidance.</p>



<p class="wp-block-paragraph">The investigation should therefore compare SQL Server observations with operating system and virtualisation data, including:</p>



<ul class="wp-block-list">

<li>CPU utilisation at operating system level</li>



<li>CPU usage by processes other than SQL Server</li>



<li>virtual CPU allocation and host contention</li>



<li>recent changes to the virtual machine or physical host</li>

</ul>



<p class="wp-block-paragraph">Hypervisor indicators such as CPU ready time may also be relevant, depending on the platform.</p>



<p class="wp-block-paragraph">A SQL Server host may show sustained CPU pressure because the workload has genuinely grown beyond the available capacity. It may also receive less physical CPU time than expected because the virtualisation platform is constrained. Those situations can look similar inside SQL Server while requiring very different actions.</p>



<p class="wp-block-paragraph">This follows the same principle discussed in <a href="https://www.craftedsql.com/sql-server-monitoring-is-not-diagnosis/">SQL Server Monitoring Is Not Diagnosis</a>: monitoring identifies signals, but diagnosis requires the surrounding operational context.</p>




<h2 class="wp-block-heading">The constraint must explain the business impact</h2>



<p class="wp-block-paragraph">A technical finding becomes useful when it explains what users or business processes actually experienced.</p>



<p class="wp-block-paragraph">Suppose CPU usage briefly reaches 95 percent during a known workload while application response times remain stable. That may simply reflect effective use of the available resources. Sustained utilisation at that level would still justify a review of capacity, workload growth and operational headroom.</p>



<p class="wp-block-paragraph">Now consider a server at 65 percent CPU while one business-critical process becomes unresponsive. The overall percentage looks less alarming, but a specific workload may still be unable to receive enough processing time or may be waiting on another constrained resource.</p>



<p class="wp-block-paragraph">The most visible metric is not always the one that best explains the incident.</p>



<p class="wp-block-paragraph">A credible diagnosis connects four elements:</p>



<ol class="wp-block-list">

<li>the symptom reported by users or monitoring,</li>



<li>the workload active at that time,</li>



<li>the technical constraint supported by the available evidence,</li>



<li>the effect of that constraint on the business process.</li>

</ol>



<p class="wp-block-paragraph">If those elements do not form a consistent chain, the proposed root cause remains an assumption.</p>




<h2 class="wp-block-heading">When high SQL Server CPU really requires more capacity</h2>



<p class="wp-block-paragraph">There are situations where additional CPU capacity is justified.</p>



<p class="wp-block-paragraph">The workload may be understood, execution plans may be appropriate, query behaviour may match legitimate business demand, and the infrastructure may no longer provide enough capacity.</p>



<p class="wp-block-paragraph">In that case, adding CPU is not a shortcut. It is a capacity decision supported by evidence.</p>



<p class="wp-block-paragraph">The sequence matters. First establish what is consuming CPU and why. Then decide whether the workload should be corrected, reduced, rescheduled or given more processing capacity.</p>



<p class="wp-block-paragraph">Instead of beginning with the question:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Does this SQL Server need more CPU?</p>
</blockquote>



<p class="wp-block-paragraph">begin with:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">What is creating the CPU pressure, and which action addresses that cause?</p>
</blockquote>



<p class="wp-block-paragraph">The answer may lead to an execution plan, a workload change, a scheduling issue, a virtualisation constraint or a legitimate need for more processing capacity.</p>



<p class="wp-block-paragraph">High CPU gives the investigation a direction. The evidence determines the action.</p>
<p>The post <a href="https://www.craftedsql.com/high-sql-server-cpu/">High SQL Server CPU Is a Signal, Not a Root Cause</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SQL Server Wait Statistics Are Not a Diagnosis</title>
		<link>https://www.craftedsql.com/sql-server-wait-statistics-context/</link>
					<comments>https://www.craftedsql.com/sql-server-wait-statistics-context/#comments</comments>
		
		<dc:creator><![CDATA[Bjoern@CraftedSQL]]></dc:creator>
		<pubDate>Thu, 09 Jul 2026 08:20:37 +0000</pubDate>
				<category><![CDATA[Performance & Optimization]]></category>
		<category><![CDATA[Performance Troubleshooting]]></category>
		<category><![CDATA[Root Cause Analysis]]></category>
		<category><![CDATA[SQL Server Performance]]></category>
		<category><![CDATA[Wait Statistics]]></category>
		<guid isPermaLink="false">https://www.craftedsql.com/?p=20984</guid>

					<description><![CDATA[<p>SQL Server wait statistics are often one of the first things people check when performance drops, and for good reason. They show where SQL Server is spending time and can provide a useful direction at the start of an investigation. The problem begins when the highest wait type becomes the diagnosis. A top wait may ... <a title="SQL Server Wait Statistics Are Not a Diagnosis" class="read-more" href="https://www.craftedsql.com/sql-server-wait-statistics-context/" aria-label="Read more about SQL Server Wait Statistics Are Not a Diagnosis">Read more</a></p>
<p>The post <a href="https://www.craftedsql.com/sql-server-wait-statistics-context/">SQL Server Wait Statistics Are Not a Diagnosis</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">SQL Server wait statistics are often one of the first things people check when performance drops, and for good reason. They show where SQL Server is spending time and can provide a useful direction at the start of an investigation.</p>



<p class="wp-block-paragraph">The problem begins when the highest wait type becomes the diagnosis.</p>



<p class="wp-block-paragraph">A top wait may point towards storage, memory, CPU, locking, parallelism or application behaviour. It cannot tell us why the problem happened or whether the wait relates to the reported incident. It also cannot tell us which technical action is justified.</p>



<p class="wp-block-paragraph">That distinction matters. When teams interpret wait statistics too quickly, they can end up tuning the wrong layer, scaling the wrong resource or debating ownership before they understand the cause.</p>




<h2 class="wp-block-heading"><strong>SQL Server wait statistics show direction, not root cause</strong></h2>



<p class="wp-block-paragraph">A high wait type can be a valuable clue, but it is rarely enough to justify a technical decision on its own.</p>



<p class="wp-block-paragraph">Consider PAGEIOLATCH waits during a performance incident. Storage might be involved, but the more useful question is why SQL Server had to read so many pages from storage in the first place.</p>



<p class="wp-block-paragraph">The explanation could be slow storage. It could also be a missing index, a changed execution plan, an unexpected table scan or memory pressure. Increased data volume or a report processing far more rows than expected can produce a similar signal.</p>



<p class="wp-block-paragraph">The wait points towards an area of the system. It does not distinguish between those causes for us.</p>



<p class="wp-block-paragraph">ASYNC_NETWORK_IO is another good example. The name makes a network problem sound like the obvious conclusion. In practice, the wait may come from a client consuming rows slowly, an application requesting too much data or a report exporting millions of rows. A query returning data that the business process does not need can create the same situation.</p>



<p class="wp-block-paragraph">The network may indeed be involved. The wait time alone simply does not prove that the network is the bottleneck.</p>



<p class="wp-block-paragraph">This is where performance investigations can become unnecessarily expensive. The problem is usually not a lack of data, and wait statistics themselves are not unreliable. The problem starts when the first visible signal triggers a technical action before anyone examines the surrounding evidence.</p>




<h2 class="wp-block-heading"><strong>Why does this become a business problem?</strong></h2>



<p class="wp-block-paragraph">From an operational perspective, choosing the wrong technical explanation is only part of the risk. The larger problem begins when a team uses that incomplete interpretation to make technical or infrastructure decisions.</p>



<p class="wp-block-paragraph">A team may invest in faster storage when an inefficient workload is the real issue. A cloud environment may receive more resources because an execution plan regressed. Teams may change parallelism settings globally because CXPACKET or CXCONSUMER appears prominently in the wait statistics, even though only a few expensive queries cause the pressure.</p>



<p class="wp-block-paragraph">Application, database and infrastructure teams may also hand the same incident back and forth. Each team can point to a signal that appears to support its own interpretation.</p>



<p class="wp-block-paragraph">None of this is unusual. SQL Server performance problems regularly cross the boundaries between database design, query behaviour, application patterns, infrastructure, virtualisation, storage and the business workload itself. Wait statistics help navigate that complexity, but only when we connect them to the right questions.</p>



<p class="wp-block-paragraph">For an IT owner, the important question is therefore not simply:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph"><em>Which wait type is at the top?</em></p>

</blockquote>



<p class="wp-block-paragraph">A more useful question is:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">

<p class="wp-block-paragraph"><em>Does this wait type explain the reported problem during the relevant time window and for the affected workload well enough to guide the next action?</em></p>

</blockquote>



<p class="wp-block-paragraph">That sets a very different standard for the investigation.</p>




<h2 class="wp-block-heading"><strong>Context changes the meaning of the same signal</strong></h2>



<p class="wp-block-paragraph">The same wait type can mean very different things depending on when it occurs and which workload is running.</p>



<p class="wp-block-paragraph">PAGEIOLATCH during a one-time reporting workload has a different meaning from PAGEIOLATCH during a normal business transaction that used to complete in milliseconds. The technical signal may look the same, while the workload behind it is completely different.</p>



<p class="wp-block-paragraph">WRITELOG during a large batch process also needs a different interpretation from WRITELOG during an OLTP workload that suddenly starts committing thousands of small transactions.</p>



<p class="wp-block-paragraph">The same principle applies to analytical workloads. ETL processing can involve large scans, joins and sorts while reporting runs at the same time. The resulting waits do not tell us whether the real problem is query execution, workload overlap, TempDB, storage or a platform approaching its capacity. The <a href="/sql-server-data-warehouse-performance/">SQL Server performance for data warehouse workloads</a> page looks at that interaction in more detail.</p>



<p class="wp-block-paragraph">It also matters when <a href="/sql-server-performance-power-bi/">Power BI uses SQL Server as part of the data path</a>. A slow report may coincide with I/O waits, CPU pressure or blocking on SQL Server. Those signals still need to match the actual report workload before they justify a database or infrastructure change.</p>



<p class="wp-block-paragraph">Parallelism waits require the same care. CXPACKET or CXCONSUMER during a large analytical query describes a different situation from hundreds of small queries going parallel because of the instance configuration.</p>




<h3 class="wp-block-heading"><strong>The context I want around wait statistics</strong></h3>



<p class="wp-block-paragraph">To make wait statistics useful, I normally want several layers of context around them.</p>



<p class="wp-block-paragraph"><strong>Time matters.</strong> Instance-level wait statistics accumulate over time unless you capture a delta or reset them. A top wait collected over days or weeks may have very little to do with an incident between 10:15 and 10:45 this morning.</p>



<p class="wp-block-paragraph"><strong>Workload matters.</strong> A month-end report, an import job, a nightly batch and a normal user transaction place very different demands on SQL Server. A wait that makes sense during one process may be suspicious during another.</p>



<p class="wp-block-paragraph"><strong>Query and plan context matter.</strong> Query Store, execution plans and runtime statistics can connect a broad wait signal to queries and plans that changed, regressed or consumed unusual resources.</p>



<p class="wp-block-paragraph"><strong>Operational context matters.</strong> Deployments, data growth, changing business behaviour, infrastructure maintenance, job schedules and application releases can all change the meaning of the same technical signal.</p>



<p class="wp-block-paragraph">Without those layers, the wait statistic is still useful. It is simply incomplete.</p>




<h2 class="wp-block-heading"><strong>A better troubleshooting sequence</strong></h2>



<p class="wp-block-paragraph">A reliable investigation does not ignore wait statistics. It uses them early, but interprets them carefully.</p>



<p class="wp-block-paragraph">I prefer to start with the reported symptom. Which application, process, user group or business function has the problem? When did it start? Is it still happening? Is the issue new, recurring or tied to a known workload pattern?</p>



<p class="wp-block-paragraph">Once that is clear, the technical signals become much easier to evaluate. Wait statistics, CPU, memory, I/O, blocking, Query Store, execution plans and application logs can all contribute evidence. Their job is to support or challenge a hypothesis, not to produce an automatic conclusion.</p>



<p class="wp-block-paragraph">A practical troubleshooting sequence often looks like this:</p>



<ol class="wp-block-list">

<li>Define the symptom and the relevant time window.</li>



<li>Identify the affected workload or business process.</li>



<li>Use wait statistics as a directional signal.</li>



<li>Connect that signal to queries, plans, sessions or workload behaviour.</li>



<li>Compare the observation with the baseline, historical behaviour and recent changes.</li>



<li>Form a hypothesis and look for evidence that could disprove it.</li>



<li>Choose the next action only when the signal has enough context.</li>

</ol>



<p class="wp-block-paragraph">At first glance, this may look slower than jumping directly from a wait type to a fix. In practice, it usually saves time.</p>



<p class="wp-block-paragraph">The approach reduces unnecessary tuning and avoids premature infrastructure decisions. It also gives different teams a common technical interpretation instead of competing assumptions.</p>




<h2 class="wp-block-heading"><strong>When a focused performance review helps</strong></h2>



<p class="wp-block-paragraph">External support is not necessary every time an unusual wait type appears. Experienced DBAs and operations teams can interpret many common patterns themselves, particularly when they know the application and understand its normal workload.</p>



<p class="wp-block-paragraph">A focused review becomes more useful when the same symptoms keep returning. It can also help when teams disagree about whether the issue belongs to SQL Server, the application or the infrastructure. Another common situation is having plenty of monitoring data but no clear next step.</p>



<p class="wp-block-paragraph">In those situations, the value is not another dashboard or a generic wait statistics report. It is an independent interpretation of the evidence.</p>



<p class="wp-block-paragraph">A structured <a href="https://www.craftedsql.com/services#sql-server-performance-analysis-troubleshooting" target="_blank" rel="noreferrer noopener">SQL Server Performance Troubleshooting review</a> can help validate assumptions and connect wait statistics to workload behaviour. It can also separate symptoms from likely causes and identify a reasonable next action before the team spends time tuning the wrong layer.</p>



<p class="wp-block-paragraph">At CraftedSQL, this work sits between raw monitoring data and operational decision-making. I interpret SQL Server signals in context, challenge premature conclusions and help teams decide what they should investigate next.</p>




<h2 class="wp-block-heading"><strong>Final thought</strong></h2>



<p class="wp-block-paragraph">SQL Server wait statistics are strong signals and often one of the best places to start a performance investigation. They are not a replacement for diagnosis.</p>



<p class="wp-block-paragraph">Good troubleshooting is not about reacting to the loudest metric. It means connecting that metric to the affected workload and the relevant time window. From there, the supporting evidence determines whether the explanation is strong enough to act on.</p>



<p class="wp-block-paragraph">A top wait tells you where to look. It does not tell you what to fix.</p>
<p>The post <a href="https://www.craftedsql.com/sql-server-wait-statistics-context/">SQL Server Wait Statistics Are Not a Diagnosis</a> appeared first on <a href="https://www.craftedsql.com">CraftedSQL</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.craftedsql.com/sql-server-wait-statistics-context/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
