> [!TIP]
> Recept z [kuchárky exekučných plánov](script.php?id=64&s=exekucne-plany-kucharka-zacni-tu). Ukážky bežia v testovacej databáze [PlanLab](script.php?id=65&s=planlab-testovacia-databaza-pre-recepty-exekucnych-planov) – nikdy ich nespúšťaj na produkcii.
## Ako to vyzerá v pláne

V SSMS: **Table Spool**, **Index Spool**, **Row Count Spool** a **Window Spool**, s logickou operáciou **Eager Spool** alebo **Lazy Spool**. Ikona pripomína valec (pracovnú tabuľku) so šípkou späť.
## Čo to znamená
Spool je **dočasné odloženie riadkov** do pracovnej tabuľky v tempdb (alebo v pamäti), aby sa nemuseli počítať znova alebo aby sa oddelilo čítanie od zápisu.
- **Eager** – spool najprv prečíta **celý** vstup a až potom posiela riadky ďalej (blokujúci).
- **Lazy** – ukladá riadky postupne, ako prichádzajú, a pri opakovanom použití ich prehrá.
### Do hĺbky
- **Table Spool** – pracovná tabuľka bez indexu. Typicky na vnútornej strane Nested Loops, keď sa ten istý vstup opakuje (*rewind* = prehraj uložené, *rebind* = hodnota sa zmenila, prepočítaj). Vlastnosti **Actual Rebinds / Actual Rewinds** ukážu pomer.
- **Index Spool** – pracovná tabuľka **s indexom**. *Eager Index Spool* na vnútornej strane Nested Loops znamená: optimalizátor si pri **každom spustení dotazu** stavia dočasný index nad celým vstupom, lebo trvalý chýba. Je to jeden z najjasnejších signálov „sem patrí index“ (často bez zeleného návrhu Missing Index).
- **Row Count Spool** – uloží len **počet** riadkov; typicky pri `NOT EXISTS` / `NOT IN` na overenie, či vnútorný vstup niečo vrátil, bez opakovaného čítania.
- **Window Spool** – pre window funkcie s rámcom (`ROWS` / `RANGE`). Predvolený rámec `RANGE` pri `ORDER BY` používa pracovnú tabuľku na disku, `ROWS` pamäť – viď [Window funkcie v pláne](script.php?id=80&s=window-funkcie-v-plane-segment-sequence-project-a-window-spool).
- **Halloween protection** – v UPDATE/INSERT/DELETE plánoch Eager Table Spool oddelí čítanie od zápisu, aby sa ten istý riadok nespracoval dvakrát (napr. keď UPDATE mení stĺpec indexu, podľa ktorého sa číta). Viď [INSERT / UPDATE / DELETE v pláne](script.php?id=82&s=insert-update-a-delete-v-plane-narrow-vs-wide-plan-assert).
- Práca spoolu sa ukáže v STATISTICS IO ako **Worktable**.
## Kedy je to v poriadku / kedy problém
| V poriadku | Problém |
|---|---|
| Lazy Table Spool s veľa rewinds a málo rebinds | Eager Index Spool nad veľkou tabuľkou (chýba index) |
| Row Count Spool pri NOT EXISTS | Table Spool s tisíckami rebinds (nič sa neušetrí) |
| Eager Spool pre Halloween protection pri malej zmene | Window Spool na disku pri veľkom počte riadkov |
## Ako si to vyskúšať
```sql
USE PlanLab;
SET STATISTICS IO ON;
-- 1) Eager Index Spool: OrderLines.ProductID nemá index
SELECT p.ProductID, x.Qty
FROM dbo.Products AS p
CROSS APPLY (SELECT TOP (1) l.Qty FROM dbo.OrderLines AS l
WHERE l.ProductID = p.ProductID ORDER BY l.Qty DESC) AS x;
-- 2) S trvalým indexom spool zmizne (porovnaj Worktable v Messages)
CREATE INDEX IX_Lab_Lines_Product ON dbo.OrderLines (ProductID, Qty);
SELECT p.ProductID, x.Qty
FROM dbo.Products AS p
CROSS APPLY (SELECT TOP (1) l.Qty FROM dbo.OrderLines AS l
WHERE l.ProductID = p.ProductID ORDER BY l.Qty DESC) AS x;
DROP INDEX IX_Lab_Lines_Product ON dbo.OrderLines;
-- 3) Halloween protection: UPDATE stĺpca, podľa ktorého sa číta (v transakcii, potom ROLLBACK)
CREATE INDEX IX_Lab_Orders_Amount ON dbo.Orders (Amount);
BEGIN TRAN;
UPDATE dbo.Orders SET Amount = Amount + 1 WHERE Amount < 12;
ROLLBACK;
DROP INDEX IX_Lab_Orders_Amount ON dbo.Orders;
```
Spusti s **Include Actual Execution Plan** (Ctrl+M). V bode 1 nájdi Index Spool (Eager) a v *Messages* riadok *Worktable*. V bode 3 hľadaj Table Spool (Eager) medzi čítaním a Index Update – ak ho optimalizátor nepoužil, zvolil iný spôsob čítania.
## Čo s tým
1. **Eager Index Spool** → vytvor trvalý index so stĺpcami zo *Seek Predicates* spoolu (a stĺpcami, ktoré vracia).
2. Table Spool s veľa rebinds → pozri, prečo sa vnútorný vstup opakovane prepočítava (korelovaný poddotaz, chýbajúci index).
3. Halloween spool pri veľkých UPDATE → zváž, či aktualizovať stĺpec indexu, podľa ktorého sa vyberá, alebo meniť dáta po dávkach.
4. Window Spool → `ROWS UNBOUNDED PRECEDING` namiesto predvoleného `RANGE`, kde to dáva rovnaký výsledok.
## Súvisiace
- [Nested Loops](script.php?id=71&s=nested-loops-vnorene-cykly-outer-a-inner-vstup)
- [Window funkcie v pláne](script.php?id=80&s=window-funkcie-v-plane-segment-sequence-project-a-window-spool)
- [INSERT / UPDATE / DELETE v pláne](script.php?id=82&s=insert-update-a-delete-v-plane-narrow-vs-wide-plan-assert)
- [Chýbajúci index v pláne](script.php?id=92&s=chybajuci-index-v-plane-zeleny-text-preco-ho-nebrat-naslepo)
- [Kto spotrebúva tempdb](script.php?id=20&s=kto-spotrebuva-tempdb)
## Zdroje
- [Microsoft Learn – Showplan Logical and Physical Operators Reference](https://learn.microsoft.com/sql/relational-databases/showplan-logical-and-physical-operators-reference)
- [Hugo Kornelis – SQL Server Execution Plan Reference (SQLServerFast)](https://sqlserverfast.com/epr/)
- [Paul White – SQLkiwi (články o optimalizátore a operátoroch)](https://www.sql.kiwi/)
- [Erik Darling – blog (plány, memory grants, parameter sniffing)](https://erikdarling.com/)
_Texty sú vlastné, tvrdenia overené proti uvedeným zdrojom (11/2026). T-SQL je staticky skontrolovaný, nie spustený na živom serveri – vyskúšaj ho najprv v PlanLab._Spools – Table, Index, Row Count a Window Spool (eager vs. lazy)
Spool operátory v pláne: eager vs. lazy, rebinds a rewinds, Eager Index Spool ako signál chýbajúceho indexu, Row Count Spool pri NOT EXISTS, Window Spool a Halloween protection.