> [!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á  Plán je vždy to isté **XML (showplan)** – líši sa len to, **kedy** a **s čím** ho dostaneš. Odhadovaný plán vznikne bez vykonania, skutočný obsahuje aj runtime čísla, živé štatistiky ukazujú beh priebežne. ## Čo to znamená Pre juniora: v SSMS stlač **Ctrl+M** a spusti dotaz – dole pribudne záložka *Execution plan*. To je skutočný plán a väčšinou stačí. Keď dotaz nemôžeš spustiť (beží hodiny, mení dáta, je na produkcii), použi plán z **plan cache** alebo **Query Store** – je odhadovaný, ale ukáže tvar plánu, ktorý server naozaj používa. ### Do hĺbky - **SET SHOWPLAN_XML ON** – relácia dotazy nevykonáva, len vráti plán. Platí pre celú dávku, preto je v ukážke medzi `GO`. - **SET STATISTICS XML ON** – dotaz sa vykoná a ako ďalší výsledok príde skutočný plán. Textová obdoba je `SET STATISTICS PROFILE ON` (tabuľka operátorov s počtami). - **Plan cache** – `sys.dm_exec_query_plan(plan_handle)` vráti plán celej dávky alebo procedúry; `sys.dm_exec_text_query_plan` s offsetmi vráti len jeden príkaz (a zvládne aj plány, ktoré sa nevojdú do typu `xml` kvôli hĺbke vnorenia). Plán z cache nemá runtime čísla – na tie je `sys.dm_exec_query_stats` (súčty za všetky vykonania). - **Posledný skutočný plán (2019+)** – `sys.dm_exec_query_plan_stats` po zapnutí `LAST_QUERY_PLAN_STATS` (database scoped configuration). Ukladá runtime čísla z posledného vykonania vďaka *lightweight profiling*-u. - **Bežiaci dotaz** – `sys.dm_exec_query_statistics_xml(session_id)` (2016 SP1+) vráti plán s doterajšími počtami. V SSMS to isté urobí *Activity Monitor* → *Active Expensive Queries* → *Show Live Execution Plan*. - **Query Store** – `sys.query_store_plan.query_plan` (nvarchar, preto `TRY_CAST(... AS xml)`). Obsahuje všetky plány dotazu v čase – základ pre [plan regression](script.php?id=95&s=plan-regression-novy-horsi-plan-a-vynutenie-cez-query-store). - **Extended Events** – `query_post_execution_plan_profile` (lightweight, 2017 CU14+ a 2019+) alebo `query_post_execution_showplan` (štandardný profiling – drahý, na produkcii len krátko a s úzkym filtrom). ## Kedy je to v poriadku / kedy problém - Odhadovaný plán z cache je **v poriadku** na zistenie tvaru (sken vs. seek, typ spojenia). **Nestačí**, keď riešiš zlé odhady – chýba v ňom skutočnosť. - `SET STATISTICS XML` a skutočný plán v SSMS pridávajú réžiu. Na krátky dotaz zanedbateľnú, pri dotaze s miliónmi riadkov cez Nested Loops môže byť čas so zapnutým plánom citeľne dlhší. - Plán z cache môže chýbať: `OPTION (RECOMPILE)`, vyhodenie z cache pod tlakom pamäte, reštart, *optimize for ad hoc workloads* (pri prvom spustení sa uloží len *stub*). ## Ako si to vyskúšať 1. V databáze [PlanLab](script.php?id=65&s=planlab-testovacia-databaza-pre-recepty-exekucnych-planov) spusti časti 1 a 2 skriptu. Klikni na XML výsledok – SSMS ho otvorí ako grafický plán. 2. Spusti časť 3 – nájdeš v nej dotaz z časti 2 a jeho plán z cache. 3. Na SQL Server 2019+ zapni v PlanLab `LAST_QUERY_PLAN_STATS`, spusti dotaz z časti 2 ešte raz a potom časť 4 – plán bude mať skutočné počty riadkov. 4. Plán ulož (pravý klik → *Save Execution Plan As…*) a otvor súbor `.sqlplan` znova – tak sa plán posiela kolegovi. ## Čo s tým 1. Na ladenie konkrétneho dotazu: **skutočný plán v SSMS** (Ctrl+M) + `SET STATISTICS IO, TIME ON`. 2. Na „čo server robí teraz“: živý plán cez `sys.dm_exec_query_statistics_xml` alebo [Dlho bežiace požiadavky](script.php?id=16&s=dlho-beziace-poziadavky-co-prave-bezi). 3. Na históriu a porovnanie plánov v čase: **Query Store** (pri SQL Server 2022+ je pre nové databázy zapnutý predvolene). 4. Na zber z produkcie bez spúšťania dotazov: posledný skutočný plán (2019+) alebo XE s `query_post_execution_plan_profile` a úzkym filtrom. ## Súvisiace - [Exekučné plány – kuchárka](script.php?id=64&s=exekucne-plany-kucharka-zacni-tu) - [SET STATISTICS IO, TIME](script.php?id=67&s=set-statistics-io-time-citanie-vystupu-vedla-planu) - [Top 20 dotazov podľa CPU](script.php?id=5&s=top-20-dotazov-podla-cpu) - [Query Store – top dotazy podľa CPU](script.php?id=25&s=query-store-top-dotazy-podla-cpu) - [Query Store – konfigurácia a stav](script.php?id=46&s=query-store-konfiguracia-a-stav) - [Výbava: SSMS](vybava.php#klienti) ## Zdroje - [Microsoft Learn – Display an Actual Execution Plan](https://learn.microsoft.com/sql/relational-databases/performance/display-an-actual-execution-plan) - [Microsoft Learn – Display the Estimated Execution Plan](https://learn.microsoft.com/sql/relational-databases/performance/display-the-estimated-execution-plan) - [Microsoft Learn – Save an Execution Plan in XML Format](https://learn.microsoft.com/sql/relational-databases/performance/save-an-execution-plan-in-xml-format) - [Microsoft Learn – sys.dm_exec_query_plan](https://learn.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-query-plan-transact-sql) - [Microsoft Learn – sys.dm_exec_query_plan_stats](https://learn.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-query-plan-stats-transact-sql) - [Microsoft Learn – sys.dm_exec_query_statistics_xml](https://learn.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-query-statistics-xml-transact-sql) - [Microsoft Learn – Query Profiling Infrastructure](https://learn.microsoft.com/sql/relational-databases/performance/query-profiling-infrastructure) - [Microsoft Learn – Monitor performance by using the Query Store](https://learn.microsoft.com/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store) _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._
Ako získať exekučný plán – SSMS, plan cache, Query Store, Extended Events
Všetky cesty k plánu: odhadovaný (Ctrl+L, SHOWPLAN_XML, plan cache), skutočný (Ctrl+M, STATISTICS XML), posledný skutočný z cache (2019+), plán bežiaceho dotazu a plány z Query Store.
/* DeadlockCafe | Ako získať exekučný plán – SSMS, plan cache, Query Store, Extended Events
Účel: Všetky cesty k plánu: odhadovaný (Ctrl+L, SHOWPLAN_XML, plan cache), skutočný (Ctrl+M, STATISTICS XML), posledný skutočný z cache (2019+), plán bežiaceho dotazu a plány z Query Store.
Režim: len čítanie | SQL Server 2016+ (časť 4: 2019+)
*/
/* 1) Odhadovaný plán bez spustenia (výsledok = XML plánu; v SSMS klikni na odkaz) */
SET SHOWPLAN_XML ON;
GO
SELECT o.OrderID, o.Amount FROM dbo.Orders AS o WHERE o.CustomerID = 42;
GO
SET SHOWPLAN_XML OFF;
GO
/* 2) Skutočný plán z relácie (dotaz sa vykoná, plán príde ako ďalší výsledok) */
SET STATISTICS XML ON;
SELECT o.OrderID, o.Amount FROM dbo.Orders AS o WHERE o.CustomerID = 42;
SET STATISTICS XML OFF;
GO
/* 3) Plány z plan cache – najdrahšie dotazy podľa CPU, plán konkrétneho príkazu */
SELECT TOP ({{Top:20|Počet dotazov}})
qs.execution_count,
qs.total_worker_time / 1000 AS cpu_ms_spolu,
qs.total_logical_reads,
SUBSTRING(st.text, qs.statement_start_offset / 2 + 1,
(CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END
- qs.statement_start_offset) / 2 + 1) AS prikaz,
TRY_CAST(tqp.query_plan AS xml) AS plan_prikazu,
qs.plan_handle
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
CROSS APPLY sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) AS tqp
ORDER BY qs.total_worker_time DESC;
GO
/* 4) Posledný SKUTOČNÝ plán z cache (SQL Server 2019+, v danej databáze treba zapnúť) */
-- ALTER DATABASE SCOPED CONFIGURATION SET LAST_QUERY_PLAN_STATS = ON; -- spusti v cieľovej databáze
SELECT TOP (20) st.text, qps.query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
CROSS APPLY sys.dm_exec_query_plan_stats(cp.plan_handle) AS qps
WHERE st.dbid = DB_ID()
ORDER BY cp.usecounts DESC;
GO
/* 5) Plán práve bežiaceho dotazu (živé čísla; 2016 SP1+, v 2019+ bez ďalšieho nastavenia) */
SELECT r.session_id, r.status, r.wait_type, r.cpu_time, r.total_elapsed_time, qx.query_plan
FROM sys.dm_exec_requests AS r
CROSS APPLY sys.dm_exec_query_statistics_xml(r.session_id) AS qx
WHERE r.session_id <> @@SPID AND r.session_id > 50;
GO
/* 6) Plány z Query Store (história; Query Store musí byť v databáze zapnutý) */
SELECT TOP (20) q.query_id, p.plan_id, qt.query_sql_text,
rs.avg_duration / 1000.0 AS avg_ms, rs.count_executions,
TRY_CAST(p.query_plan AS xml) AS query_plan, p.is_forced_plan
FROM sys.query_store_query AS q
JOIN sys.query_store_query_text AS qt ON qt.query_text_id = q.query_text_id
JOIN sys.query_store_plan AS p ON p.query_id = q.query_id
JOIN sys.query_store_runtime_stats AS rs ON rs.plan_id = p.plan_id
ORDER BY rs.avg_duration DESC;