{"id":926,"date":"2010-06-25T00:00:00","date_gmt":"2010-06-25T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/operator-of-the-week-index-spool\/"},"modified":"2021-08-16T15:02:14","modified_gmt":"2021-08-16T15:02:14","slug":"operator-of-the-week-index-spool","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/operator-of-the-week-index-spool\/","title":{"rendered":"Operator of the Week &#8211; Index Spool"},"content":{"rendered":"<div id=\"pretty\">\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-spool_icon.gif\" alt=\"1073-spool_icon.gif\" \/><br \/>\n <strong>Nonclustered <br \/>\n Index Spool<\/strong><\/p>\n<p>Indexes, Indexes, Indexes&#8230; To misquote a certain prominent Microsoft employee. Indexing is a key issue when we are talking about databases and general performance problems, and so it&#8217;s time to feature the NonClustered Index Spool in my SQL Server ShowPlan operator series. If you missed the last article about the <a href=\"http:\/\/www.simple-talk.com\/sql\/learn-sql-server\/showplan-operator-of-the-week---lazy-spool\/\">Lazy Spool<\/a>, it&#8217;s actually very important that you read that before you get too deeply into this next article. If you&#8217;re just getting started with my Showplan series, you can find a list of all my articles <a href=\"http:\/\/www.simple-talk.com\/author\/fabiano-amorim\/\">here<\/a>.<\/p>\n<p>The Index Spool is used to improve the read performance of a table which is not indexed and, as with other types of Spool operators, it can be used in a &#8220;Lazy&#8221; or an &#8220;Eager&#8221; manner. So, when SQL Server needs to read a table that is not indexed, it can choose to create a &#8220;temporary index&#8221; using the Spool, which can result in a huge performance improvement in your queries. To get started with understanding Index Spool, we&#8217;ll use the usual table, called Pedidos (which means &#8220;Orders&#8221; in Portuguese). The following script will create a table and populate it with some garbage data:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">USE tempdb\r\nGO\r\nIF OBJECT_ID('Pedido') IS NOT NULL\r\n\u00a0\u00a0DROP TABLE Pedido\r\nGO\r\nCREATE TABLE Pedido (ID\u00a0INT IDENTITY(1,1),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Cliente\u00a0INT NOT NULL,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Vendedor\u00a0VARCHAR(30) NOT NULL,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Quantidade SmallInt NOT NULL,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Valor\u00a0Numeric(18,2) NOT NULL,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Data\u00a0DATETIME NOT NULL)\r\nGO\r\nCREATE CLUSTERED INDEX ix ON Pedido(ID)\r\nGO\r\nDECLARE @I SmallInt\r\n\u00a0\u00a0SET @I = 0\r\nWHILE @I &lt; 50\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0INSERT INTO Pedido(Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0\u00a0\u00a0SELECT ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0\u00a0\u00a0'Fabiano',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0GETDATE() - (CheckSUM(NEWID()) \/ 1000000)\r\n\u00a0\u00a0INSERT INTO Pedido(Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0\u00a0\u00a0SELECT ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0\u00a0\u00a0'Amorim',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0GETDATE() - (CheckSUM(NEWID()) \/ 1000000)\r\n\u00a0\u00a0INSERT INTO Pedido(Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0\u00a0\u00a0SELECT ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0\u00a0\u00a0'Coragem',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0GETDATE() - (CheckSUM(NEWID()) \/ 1000000)\r\n\u00a0\u00a0\u00a0\u00a0SET @I = @I + 1\u00a0\r\n\u00a0\u00a0END\r\nSET @I = 0\r\nWHILE @I &lt; 2\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0INSERT INTO Pedido(Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0\u00a0\u00a0SELECT Cliente, Vendedor, Quantidade, Valor, Data\r\n\u00a0\u00a0\u00a0\u00a0FROM Pedido\r\n\u00a0\u00a0SET @I = @I + 1\u00a0\r\n\u00a0\u00a0END\r\nGO\u00a0\r\nSELECT *\r\n\u00a0\u00a0FROM Pedido Ped1\r\n\u00a0\u00a0WHERE Ped1.Valor &gt; (\r\n\u00a0\u00a0SELECT AVG(Ped2.Valor)\r\n\u00a0\u00a0\u00a0\u00a0FROM Pedido AS Ped2\r\n\u00a0\u00a0\u00a0\u00a0WHERE Ped2.Data &lt; Ped1.Data)<\/pre>\n<p>This is what the data looks like in SSMS:<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-demo1.gif\" alt=\"1073-demo1.gif\" width=\"474\" height=\"305\" \/><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 1. The Pedidos demonstration table and data.<\/strong><\/p>\n<p>To help us understand the Index Spool, I&#8217;ve written a query that returns all orders with a sale value higher than the average, as compared to all sales before the date of the order in question:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT *\r\n\u00a0\u00a0FROM Pedido Ped1\r\n\u00a0\u00a0WHERE Ped1.Valor &gt; (\r\n\u00a0\u00a0SELECT AVG(Ped2.Valor)\r\n\u00a0\u00a0\u00a0\u00a0FROM Pedido AS Ped2\r\n\u00a0\u00a0\u00a0\u00a0WHERE Ped2.Data &lt; Ped1.Data) <\/pre>\n<p>Before we see the execution plan, let&#8217;s make sure we understand the query a little better. The SubQuery returns the average value of all sales (<strong>AVG(Ped2.Valor)<\/strong>) dated before the order we&#8217;re comparing them to. After that, the average is compared with the principal query, which determines whether the sale value in question is actually bigger than the average. If you&#8217;ve read last week&#8217;s article, you&#8217;ll see that this query has a very similar form to my previous demonstration. So, now we&#8217;ve got the following execution plan:<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-ExPlan1.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-ExPlan1_sml.gif\" alt=\"1073-ExPlan1_sml.gif\" width=\"630\" height=\"175\" \/><\/a><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 2. The graphical execution plan of the Index Spool demonstration query (click on the image for an enlarged view).<\/strong><\/p>\n<pre>|--Filter(WHERE:([Pedido].[Valor] as [Ped1].[Valor]&gt;[Expr1004]))\r\n\u00a0\u00a0\u00a0\u00a0 |--Nested Loops(Inner Join, OUTER REFERENCES:([Ped1].[Data]))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Clustered Index Scan(OBJECT:([Pedido].[PK_Pedido] AS [Ped1]))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Index Spool(SEEK:([Ped1].[Data]=[Pedido].[Data] as [Ped1].[Data]))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012]\/CONVERT_IMPLICIT(numeric(19,0),[Expr1011],0) END))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Stream Aggregate(DEFINE:([Expr1011]=Count(*), [Expr1012]=SUM([Pedido].[Valor] as [Ped2].[Valor])))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Index Spool(SEEK:([Ped2].[Data] &lt; [Pedido].[Data] as [Ped1].[Data]))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |--Clustered Index Scan(OBJECT:([Pedido].[PK_Pedido] AS [Ped2]))\r\n<\/pre>\n<p>This is a very interesting plan, and, as we can see, the Optimizer chose to use two Index Spool operators; one working as an Eager and the other as a Lazy spool. We can also see that, after the Nested Loops, SQL Server uses the Filter operator to select just the rows that satisfy the WHERE condition (<strong>WHERE PEd1.Valor &gt; <\/strong>&#8230;).<br \/>\n This execution plan is actually simpler than it looks; first, the Clustered Index Scan reads the rows in the <strong>Pedidos<\/strong> table, returning the <strong>Data<\/strong> and <strong>Valor<\/strong> (Date and Value) columns to the Eager Index Spool. With these rows, the Optimizer uses the Index Spool to create a temporary non-clustered index on <strong>Data<\/strong> and <strong>Valor<\/strong>, and as it is an Eager spool, it will read all the rows from the clustered scan to create the index.<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Quick Tip:<\/strong> <br \/>\n <em>If you create an index on the Pedidos table <\/em><em>covering Data and Valor, you will optimizer the query because the operator Index Spool (Eager) will not be necessary.<\/em><\/p>\n<\/div>\n<p>After that, the optimizer will calculate the average value of the sales, using the following rule: For each row of Ped1, the optimizer computes the average of any orders where the Ped2.Data is lower than Ped1.Data (i.e. the average of any orders which have a date earlier than the order in the given row of Ped1). To do this, SQL Server uses the Stream Aggregate and the Compute Scalar operators, in a manner similar to <a href=\"http:\/\/www.simple-talk.com\/sql\/learn-sql-server\/showplan-operator-of-the-week---lazy-spool\/\">what was discussed last week<\/a>.<\/p>\n<p>I&#8217;ll explain the Index Spool (lazy) in just a moment, but for now I&#8217;ll just say that it optimizes the Nested Loops join, which is joining the average calculated by the sub-query to the Ped1.Data column, and the result, as I mentioned, is then filtered to complete the query.<\/p>\n<p>Now, let&#8217;s look at what makes the Index Spool (lazy) operator special. When SQL Server needs to read a value that it knows is repeated many times, then it can use a Spool to avoid having to do the same work each time it needs to find that value. For instance, suppose that the date column has a high <a href=\"http:\/\/www.simple-talk.com\/sql\/t-sql-programming\/13-things-you-should-know-about-statistics-and-the-query-optimizer\/\">density<\/a> (i.e. it contains a lot of duplicated values); this will mean that SQL Server will have to do the same calculation more than once, since the Nested Loops operator will process the join row by row. However, If the value passed as a condition to the join is equal to a value that has already been calculated, you clearly shouldn&#8217;t need to recalculate the same result each time. So how can we reuse the value that has already been found?<\/p>\n<p>This is exactly what the Nonclustered Index Spool operator (lazy), is designed to do: optimize the process of the Join. It is optimized to predict precisely the case that I&#8217;ve described above, so that a value that has already been calculated will not be <em>recalculated<\/em>, but instead read from the index, which is cached (TempDB). So, from the point of view of the Spool, it is very important to know if the required value still needs to be calculated(rebind), or has already <em>been<\/em> calculated (rewind). Now that this simple example has illustrated that point, it&#8217;s time to dive a little deeper.<\/p>\n<h1><strong>Understanding Rebind and Rewind<\/strong><\/h1>\n<p>First of all you need understand that all types of Spool operators use temporary storage to cache the values used in the execution plan, although this temporary storage is truncated for each new read of the operator. This means that, if I use a <a href=\"http:\/\/www.simple-talk.com\/sql\/learn-sql-server\/showplan-operator-of-the-week---lazy-spool\/\">Lazy Spool<\/a> to calculate an aggregation and keep this calculated value in cache, I can use this cached data in many parts of the plan, and potentially work with just single chunk of data in all the plan&#8217;s steps. However, to do this, we need reset the cache for each newly calculated value , otherwise by the end of the plan we&#8217;ll be working with the whole table! Thus, for a spool, it is very important to distinguish between executions need the <em>same<\/em> value (rewinds) and executions needing an <em>different \/ new<\/em> value (rebinds).<br \/>\n A rewind is defined as an execution using the same value as the immediately preceding execution, whereas a rebind is defined as an execution using a different value. I know this is a little confusing to understand for the first time, so I&#8217;ll try and explain it step by step, with some code and practical examples.<\/p>\n<h2><strong>Rebind and Rewinds with <\/strong><strong>Table Spool<\/strong><strong> (Lazy <\/strong><strong>Spool<\/strong><strong>)<\/strong><\/h2>\n<p>To understand a little more about Rebind and Rewind, let&#8217;s suppose our <strong>Pedido<\/strong> table has some rows in the <strong>Data<\/strong> (Date) column in the following order: &#8220;19831203&#8221;, &#8220;19831203&#8221;, &#8220;20102206&#8221; and &#8220;19831203&#8221;. A representation of Rewind and Rebind in a table spool operator would be something like this:<\/p>\n<ul>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A rebind occurs, since is the first time the operator is called.<\/li>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A rewind occurs since this value was already read, and is in the spool cache.<\/li>\n<li><strong>Value = &#8220;20102206&#8221;<\/strong>. The value changes, so the cache is truncated and a rebind occurs, since is the value &#8220;20102206&#8221; is not in the cache.<\/li>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A rebind occurs again, since the actual value in cache is &#8220;20100226&#8221;, and the value that was read in step 1 was truncated in the step 3.<\/li>\n<\/ul>\n<p>So our final numbers are <strong>three rebinds<\/strong> (steps 1, 3 and 4) and just <strong>one rewind<\/strong> (step 2). To show this in a practice, I&#8217;ve written a script to repopulate the table <strong>Pedido<\/strong> with four rows, exactly as I&#8217;ve mentioned above.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">USE tempdb\r\nGO\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0TRUNCATE TABLE Pedido\r\nGO\r\nSET IDENTITY_INSERT Pedido ON\r\nINSERT INTO Pedido(ID, Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0SELECT 1,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0'Fabiano',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0'19831203'\r\nINSERT INTO Pedido(ID, Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0SELECT 2,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0'Fabiano',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0'19831203'\r\nINSERT INTO Pedido(ID, Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0SELECT 3,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0'Fabiano',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0'20100622'\r\nINSERT INTO Pedido(ID, Cliente, Vendedor, Quantidade, Valor, Data)\r\n\u00a0\u00a0SELECT 4,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 100000000),\r\n\u00a0\u00a0'Fabiano',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CheckSUM(NEWID()) \/ 10000000),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ABS(CONVERT(Numeric(18,2), (CheckSUM(NEWID()) \/ 1000000.5))),\r\n\u00a0\u00a0'19831203'\r\n\u00a0\u00a0SET IDENTITY_INSERT Pedido OFF\r\nGO\r\n<\/pre>\n<p>This is what the data looks like in SSMS:<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-demo2.gif\" alt=\"1073-demo2.gif\" width=\"469\" height=\"134\" \/><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 3. The second Pedidos demonstration table and data.<\/strong><\/p>\n<p>To illustrate the Rebind and Rewind using the Table Spool operator (which we <a href=\"http:\/\/www.simple-talk.com\/sql\/learn-sql-server\/showplan-operator-of-the-week---lazy-spool\/\">already understand)<\/a>, I&#8217;ve written a query using the <strong>USE PLAN<\/strong> hint, to force a plan that uses the Table Spool operator. I&#8217;ll omit part of the code for brevity, but you can <a href=\"http:\/\/www.simple-talk.com\/content\/file.ashx?file=3820\">download the query here<\/a>. Note that the following query only runs in the tempdb database, because the XML plan is using this database.<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-Query1.sql\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-query1.gif\" alt=\"1073-query1.gif\" width=\"630\" height=\"257\" \/><\/a><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 4. A query to demonstrate Rewinds and Rebinds.<\/strong><\/p>\n<p>For query above, we have the following execution plan:<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-ExPlanDemo1.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-ExPlanDemo1_sml.gif\" alt=\"1073-ExPlanDemo1_sml.gif\" width=\"630\" height=\"122\" \/><\/a><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 5. The Graphical execution plan demonstrating rewinds and rebinds (click on the image for an enlarged view)<\/strong><\/p>\n<p>Note that the plan above is using a Table Spool (Lazy Spool) to perform the query; for each row read in the Pedido (Ped1) table, SQL Server will call the table Spool to run the SubQuery. Let&#8217;s look at the rebind and rewind properties to see how many times SQL Server executes each task.<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-rewind_bind1.gif\" alt=\"1073-rewind_bind1.gif\" width=\"366\" height=\"381\" \/><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 6. The rebinds and rewinds performed by the Table Spool operator.<\/strong><\/p>\n<p class=\"indented\">&#8220;<em>Hey, wait a minute Fabiano,<\/em>&#8221; &#8211; I hear you exclaim &#8211; &#8220;<em>Didn&#8217;t you said that we have three rebinds and one rewind? Why SQL is showing different values?<\/em>&#8220;<\/p>\n<p>Sharp-eyed as ever, dear reader. Pay attention, now; do you notice that I marked the Sort operator with a green square? We should be asking ourselves what this is doing.<\/p>\n<p>As I said earlier, to SQL Server, it is very important to distinguish between executions using the same value and executions using different values. That means that if SQL Server reads the <strong>Pedido<\/strong> table and <strong>Sorts<\/strong> the values by Date, it will increase the chance of a rewind occurring, because the order of rows will go from:<\/p>\n<ul>\n<li>19831203<\/li>\n<li>19831203<\/li>\n<li><em>20102206<\/em><\/li>\n<li>19831203<\/li>\n<\/ul>\n<p>to<\/p>\n<ul>\n<li>19831203<\/li>\n<li>19831203<\/li>\n<li>19831203<\/li>\n<li><em>20102206<\/em><\/li>\n<\/ul>\n<p><em>That&#8217;s<\/em> why SQL Server only makes two rebinds and two rewinds. Pretty smart, huh?<\/p>\n<h2><strong>Rebind and Rewinds with Index Spool (Lazy Spool)<\/strong><\/h2>\n<p>Now let&#8217;s see how the <strong>Index Spool<\/strong> works. Remember, index spool <em>doesn&#8217;t<\/em> truncate its cache, even if a Rebind occurs; instead it maintains a temporary index with all &#8220;rebound&#8221; rows. So a representation of Rewind and Rebind in an index spool operator would be something like this:<\/p>\n<ul>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A rebind occurs, as this is the first time the operator is called.<\/li>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A rewind occurs, as this value was already read, and is in the spool cache.<\/li>\n<li><strong>Value = &#8220;20102206&#8221;<\/strong>. A rebind occurs, as the value &#8220;20102206&#8221; is not in the cache.<\/li>\n<li><strong>Value = &#8220;19831203&#8221;<\/strong>. A <em>rewind<\/em> occurs, as this value was read in step 1, and is still in the temporary index.<\/li>\n<\/ul>\n<p>So our final numbers are <strong>two rebinds<\/strong> (steps 1 and 3) and just <strong>two rewinds<\/strong> (step 2 and 4). The same numbers used in the plan above with the table spool operator.<\/p>\n<p>To illustrate Rebind and Rewind using the Index Spool operator, I&#8217;ve written a second query using the <strong>USE PLAN<\/strong> hint to force a plan that uses the Index Spool operator. As before, I&#8217;ll omit part of the code for the sake of brevity, but you can <a href=\"http:\/\/www.simple-talk.com\/content\/file.ashx?file=3821\">download the query here<\/a>.<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-Query2.sql\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-query2.gif\" alt=\"1073-query2.gif\" width=\"630\" height=\"260\" \/><\/a><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 7. A SQL query to demonstrate rewinding and rebinding with the index spool operator.<\/strong><\/p>\n<p>To query above we have the following execution plan:<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-ExPlanDemo2_sml.gif\" alt=\"1073-ExPlanDemo2_sml.gif\" width=\"630\" height=\"175\" \/><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 8. The execution plan generated in response to the query in Figure 7 (click on the image for an enlarged view).<\/strong><\/p>\n<p>Note that, as intended, the plan above is using an Index Spool (Lazy Spool) to perform the query. For each row read in the <strong>Pedido (Ped1)<\/strong> table, SQL Server will call the Index Spool to run the SubQuery. Let&#8217;s look at the Spool&#8217;s rebind and rewind properties to see how many times SQL Server executes each task.<\/p>\n<div class=\"illustration\">\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1073-rewind_bind2.gif\" alt=\"1073-rewind_bind2.gif\" width=\"347\" height=\"427\" \/><\/p>\n<\/div>\n<p class=\"caption\"><strong>Figure 9. The number of rebinds and rewinds performed by the Index Spool.<\/strong><\/p>\n<p>Maybe you are wondering, &#8220;<em>Once again, Fabiano &#8211; your prediction is different from the real values. Could you explain that please? Why is operator&#8217;s properties showing three rebinds and one rewind, when you expected two rebinds and two rewinds?<\/em>&#8220;<\/p>\n<p>Well, this time it&#8217;s because our Microsoft friends like to confuse us. That&#8217;s right, my count is correct, and the displayed properties are actually wrong. To quote from &#8220;<strong>Inside Microsoft SQL Server 2005 Query Tuning and Optimization<\/strong>&#8220;, this is what <a href=\"http:\/\/blogs.msdn.com\/b\/craigfr\/\">Craig Freedman<\/a> wrote about this situation:<\/p>\n<p class=\"indented\">&#8220;<em>Note that rewinds and rebinds are counted the same way for index and nonindex spools. As described previously, a reexecution is counted as a rewind only if the correlated parameter(s) remain the same as the immediately prior execution, and is counted as a rebind if the correlated parameter(s) change from the prior execution. This is true even for reexecutions, in which the same correlated parameter(s) were encountered in an earlier, though not the immediately prior, execution. However, since lazy index spools, like the one in this example, retain results for all prior executions and all previously encountered correlated parameter values, the spool may treat some reported rebinds as rewinds. In other words, by failing to account for correlated parameter(s) that were seen prior to the most recent execution, the query plan statistics may overreport the number of rebinds for an index spool.<\/em>&#8220;<\/p>\n<h1><strong>Summary<\/strong><\/h1>\n<p>Generally, if you see a spool operator in your plan, you should take a closer look, because it can probably be optimized if you create the indexes properly. Doing this avoids the need for recalculations, prevents the query optimizer from having to create the indexes for you, and your query will perform better.<\/p>\n<p>In my next article, we will talk about the last spool operator, the RowCount Spool. That&#8217;s all for now folks; I&#8217;ll see you next week with more &#8220;Showplan Operators&#8221;.<\/p>\n<p class=\"note\">If you missed the last thrilling Showplan Operator, Lazy Spool, you can <a href=\"http:\/\/www.simple-talk.com\/sql\/learn-sql-server\/showplan-operator-of-the-week---lazy-spool\/\">see it here<\/a>.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;ve ever wondered what happens to your queries if you&#8217;re not conscientious about creating indexes, then Fabiano Amorim has yet another nugget of Execution Plan wisdom for you. The Query Optimizer&#8217;s solution is to use the Nonclustered Index Spool, and we&#8217;re about to learn why.&hellip;<\/p>\n","protected":false},"author":65554,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143525],"tags":[5238,4178,5213,5237,4149,5239,5192,4150,4151],"coauthors":[6809],"class_list":["post-926","post","type-post","status-publish","format-standard","hentry","category-learn","tag-amorim","tag-bi","tag-execution-plan","tag-fabiano","tag-learn-sql-server","tag-nonclustered-index","tag-showplan-operators","tag-sql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/926","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/65554"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=926"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/926\/revisions"}],"predecessor-version":[{"id":74796,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/926\/revisions\/74796"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=926"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=926"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=926"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=926"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}