SQL Server Performance Tuning! ...a SQL Server tuning story: Avoid That Damn Table Spool!

SQL Server Performance Tuning – a SQL Server tuning story: Avoid That Damn Table Spool!

SQL Server Performance Series – Execution Plan Deep Dive

Hi SQL Server Guys, 

Welcome to a SQL Server tuning story.

This post is inspired by something that happens very often in real projects:

a query that technically works… but performs terribly.

During my work I spent a lot of time optimizing queries, sometimes by adding indexes, sometimes by rewriting them.

And the interesting part is that sometimes a very small rewrite can completely change the execution plan.

Today's example is exactly one of those situations.

Enjoy the reading! 👍


PS: In case you missed my last post: The SQL Server Query That Looks Fast ….Until It Hits Production. Why?



The Problem: Table Spool

A few days ago I analyzed a query whose execution time was considered too slow by a client.

When looking at the execution plan, one operator immediately caught my attention:

Table Spool

Table Spool operators are not always bad.

But in many cases they indicate that SQL Server is forced to store intermediate results in a temporary structure, which can become expensive.


Reproducing the Scenario

First let's create a small environment to reproduce the problem.


Create table PriceList 
(
    Id int identity(1,1) primary key,
    Code varchar(20)
)

Insert into PriceList(code)
select top 1000 right('0000'+cast(row_number() over(order by id) as varchar(4)),4)
from artico

Insert into PriceList(code)
select top 4000 right('1000'+cast(row_number() over(order by id) as varchar(4)),4)
from artico

Now the Orders table.


Create table OrdersList
(
    Id int identity(1,1) primary key,
    Idpl_Normal int,
    Idpl_Special int,
    Idcustomer int
)

Insert into OrdersList(idpl_Normal,Idpl_Special,idcustomer) values (1,2,1)
Insert into OrdersList(idpl_Normal,Idpl_Special,idcustomer) values (1,2,2)
go 100

Insert into OrdersList(idpl_Normal,Idpl_Special,idcustomer) values (1,2,3)
go 200

Insert into OrdersList(idpl_Normal,Idpl_Special,idcustomer) values (1,2,4)
go 9699


The Problematic Query

Here is the original query:


Select 
  id
From PriceList n
Where Exists
(
    Select d.Id
    From OrdersList d
    Where (d.idcustomer = 1)
    and
    (
        (d.idpl_Normal = n.ID)
        or
        (d.Idpl_Special = n.ID)
    )
)

Looking at the execution plan we see something interesting.

  • OrdersList table scan
  • Table Spool operator

The Table Spool alone is responsible for about 87% of the execution time.




Logical Reads

Pages read for the original query:


Table 'OrdersList'. logical reads 33
Table 'PriceList'. logical reads 15

The query works — but the execution plan is clearly not optimal.


Rewriting the Query

Now let's try a slightly different formulation.


select id
from PriceList n
where exists
(
    select d.Id
    from OrdersList d
    where d.idcustomer = 1
    and d.idpl_Normal = n.ID
)
or exists
(
    select d.Id
    from OrdersList d
    where d.idcustomer = 1
    and d.Idpl_Special = n.ID
)

The logic is identical.

But the execution plan changes dramatically.




The Table Spool operator disappears.


Logical Reads After Rewrite


Table 'OrdersList'. logical reads 66
Table 'PriceList'. logical reads 4

Yes, the OrdersList table is read twice.

But the removal of the Table Spool drastically improves performance.


Query Cost Comparison

Estimated cost comparison:

  • Original Query: 0.594
  • Rewritten Query: 0.097

That is a huge difference.

A quick benchmark showed around:




~60% faster execution time


The Real Lesson

SQL Server performance tuning is often about experimentation.

Small changes in query structure can produce very different execution plans.

And sometimes the optimizer chooses a plan that is not the best possible one.

That is where experience and a bit of creativity help.


Final Takeaway

Table Spool operators are not always bad.

But when you see one dominating the execution plan cost, it is often worth testing alternative query formulations.

Sometimes a simple rewrite is enough to dramatically improve performance.


*** P.S. If Table Spool operators are not always bad, OR operators can represent a problem. An OR operator can led SQL SERVER to use an index in scan mode and not in seek mode... We will see in the next posts how to solve this... ***



And remember…

No SQL Server was harmed during these benchmarks 😉

See you in the next deep dive!

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!