Preskočiť na obsah
Návod

Plán a čakania – WaitStats a QueryTimeStats v skutočnom pláne

Čakania priamo v exekučnom pláne: WaitStats a QueryTimeStats (CPU vs. elapsed) na SELECT, ktoré čakania ukazujú na plán (PAGEIOLATCH, RESOURCE_SEMAPHORE, CXPACKET) a ktoré nie.

5 min čítania 4 kroky 1 obrázok SQL Server 2016+
> [!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

![WaitStats v skutočnom pláne](uploads/2026/11/plan-w1.svg "Čakania dotazu priamo v pláne – a ktoré z nich vedú späť k plánu")

V **skutočnom** pláne na koreňovom SELECT (F4): **WaitStats** – zoznam čakaní dotazu (typ, čas, počet) a **QueryTimeStats** – *CpuTime* a *ElapsedTime*. Sú tam od SQL Server 2016 SP1 (a v novších verziách SSMS aj prehľadne v Properties).

## Čo to znamená

Elapsed čas dotazu = práca (CPU) + **čakanie**. Keď je elapsed oveľa väčší ako CPU, dotaz väčšinu času čakal – a WaitStats povie na čo. Niektoré čakania ukazujú **na plán** (čítanie z disku kvôli skenu, čakanie na pamäť kvôli veľkému grantu), iné **mimo plánu** (iná session drží riadky, klient pomaly preberá výsledky). Podrobné vysvetlenie jednotlivých typov je na stránke [Čakania](waits.php).

### Do hĺbky – čakanie → kde hľadať v pláne

| Čakanie | Čo to znamená pre plán | Recept |
|---|---|---|
| [PAGEIOLATCH_SH / _EX](waits.php#PAGEIOLATCH_SH) | stránky sa čítali z disku – veľa čítaní | [Scan](script.php?id=68&s=table-scan-a-clustered-index-scan-citanie-celej-tabulky), [Key Lookup](script.php?id=70&s=key-lookup-a-rid-lookup-dohladanie-chybajucich-stlpcov), [Non-SARGable](script.php?id=89&s=non-sargable-podmienky-funkcia-na-stlpci-like-x-or) |
| [RESOURCE_SEMAPHORE](waits.php#RESOURCE_SEMAPHORE) | čakanie na memory grant | [Memory grant](script.php?id=91&s=memory-grant-kolko-pamate-dostal-dotaz-privela-alebo-primalo), [Spill](script.php?id=90&s=spill-do-tempdb-sort-a-hash-warning-memory-grant-feedback) |
| [CXPACKET](waits.php#CXPACKET) / [CXCONSUMER](waits.php#CXCONSUMER) | výmena riadkov medzi vláknami | [Paralelizmus v pláne](script.php?id=79&s=paralelizmus-v-plane-distribute-repartition-a-gather-streams), [cost threshold, MAXDOP](script.php?id=93&s=paralelizmus-cost-threshold-maxdop-a-cxpacket-cxconsumer) |
| [SOS_SCHEDULER_YIELD](waits.php#SOS_SCHEDULER_YIELD) | dotaz veľa počíta (CPU) a strieda sa s inými | [RBAR](script.php?id=94&s=rbar-kurzory-cykly-a-funkcie-po-riadkoch-v-plane), [Funkcie](script.php?id=83&s=funkcie-v-plane-scalar-udf-inlining-2019-a-table-valued-funkcie), [Hash Match](script.php?id=73&s=hash-match-hash-join-a-hash-aggregate-build-a-probe) |
| [LCK_M_S](waits.php#LCK_M_S), [LCK_M_X](waits.php#LCK_M_X)… | čakanie na inú session – **plán nie je príčina** | súbeh transakcií, [Otvorené transakcie](script.php?id=15&s=otvorene-transakcie-kto-ich-drzi-a-ako-dlho) |
| [ASYNC_NETWORK_IO](waits.php#ASYNC_NETWORK_IO) | klient preberá výsledok pomaly | menej riadkov / stĺpcov do klienta |
| [WRITELOG](waits.php#WRITELOG) | čakanie na zápis transakčného logu pri commit | [INSERT / UPDATE / DELETE](script.php?id=82&s=insert-update-a-delete-v-plane-narrow-vs-wide-plan-assert) (dávky) |

- WaitStats v pláne obsahuje len **najvýznamnejšie** čakania dotazu, nie všetky. Na celý server použi [Wait stats – na čo server čaká](script.php?id=6&s=wait-stats-na-co-server-caka).
- Čakania v pláne sú za **jedno vykonanie** (to, z ktorého je skutočný plán). Pri inom behu môžu byť iné (studená cache, iná záťaž).
- Live: čakanie bežiaceho dotazu ukáže `sys.dm_exec_requests.wait_type` alebo [Aktuálne čakania na úrovni taskov](script.php?id=17&s=aktualne-cakania-na-urovni-taskov); čakania session od jej začiatku `sys.dm_exec_session_wait_stats` (2016+).

## Kedy je to v poriadku / kedy problém

| V poriadku | Problém |
|---|---|
| elapsed ≈ CPU (dotaz pracuje, nečaká) | elapsed ≫ CPU a čakanie súvisí s plánom (I/O, grant) |
| PAGEIOLATCH pri prvom behu so studenou cache | PAGEIOLATCH pri každom behu (stále sa číta priveľa) |
| | LCK_M_* – rieš transakcie, nie index |

## Ako si to vyskúšať

```sql
USE PlanLab;
-- 1) Studená cache len pre test (DBCC DROPCLEANBUFFERS čistí buffer pool CELÉHO servera – NIKDY na produkcii!)
-- CHECKPOINT; DBCC DROPCLEANBUFFERS;
SELECT COUNT(*), SUM(Amount) FROM dbo.Orders WHERE Note = 'expres';   -- pozri WaitStats: PAGEIOLATCH_SH

-- 2) Čakanie na inú session: okno A drží zmenený riadok, okno B ho číta
-- okno A:  BEGIN TRAN; UPDATE dbo.Orders SET Amount = Amount WHERE OrderID = 10;
-- okno B:  SELECT Amount FROM dbo.Orders WHERE OrderID = 10;   -- čaká (LCK_M_S)
-- okno A:  ROLLBACK;   → okno B dobehne; v jeho skutočnom pláne je LCK_M_S vo WaitStats

-- 3) Čakania tejto session od jej začiatku (2016+)
SELECT wait_type, waiting_tasks_count, wait_time_ms
FROM sys.dm_exec_session_wait_stats WHERE session_id = @@SPID ORDER BY wait_time_ms DESC;
```

Spusti s **Include Actual Execution Plan** (Ctrl+M) a na SELECT otvor F4 → *WaitStats* a *QueryTimeStats*. Bod 1 s DBCC DROPCLEANBUFFERS rob len na vlastnom testovacom serveri.

## Čo s tým

1. Najprv porovnaj **CPU a elapsed** – rozhodne, či ladiť prácu (plán) alebo čakanie.
2. Čakanie súvisiace s plánom (I/O, grant, paralelizmus) → recept z tabuľky vyššie.
3. Čakanie mimo plánu (LCK_M_*, ASYNC_NETWORK_IO, WRITELOG) → transakcie, aplikácia, disk logu.
4. Vždy over na celom serveri: [Wait stats – rozdiel za N sekúnd](script.php?id=49&s=wait-stats-rozdiel-za-n-sekund), [Server je pomalý – prvých 10 minút](script.php?id=59&s=server-je-pomaly-prvych-10-minut).

## Súvisiace

- [Čakania – vysvetlenie typov](waits.php)
- [Wait stats – na čo server čaká](script.php?id=6&s=wait-stats-na-co-server-caka)
- [Aktuálne čakania na úrovni taskov](script.php?id=17&s=aktualne-cakania-na-urovni-taskov)
- [Exekučné plány – kuchárka](script.php?id=64&s=exekucne-plany-kucharka-zacni-tu)
- [Memory grant](script.php?id=91&s=memory-grant-kolko-pamate-dostal-dotaz-privela-alebo-primalo)

## Zdroje

- [Microsoft Learn – sys.dm_os_wait_stats](https://learn.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-os-wait-stats-transact-sql)
- [Microsoft Learn – Display an Actual Execution Plan](https://learn.microsoft.com/sql/relational-databases/performance/display-an-actual-execution-plan)
- [Brent Ozar – blog a nástroje First Responder Kit](https://www.brentozar.com/)
- [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._