Query Timeout? Možná děláš těch TOP 1 000 „SELECT *“ příliš
Devátá hodina, uživatel klikne na „Report za Q1“ a SQL Server mu po 30 s vyplivne Timeout expired. Zní povědomě? V 90 % případů nejde o nedostatek CPU, ale o příliš velké SELECT *. V tomto článku rozlouskneme, jak tajně škodí hvězdička, proč TOP 1000 není alibi, jak odhalit pomalé plány a zkrátit dotazy z minut na milisekundy.
1 · Co je query timeout a kde se nastavuje
Query timeout je ochranná lhůta klienta (application, driver), po které ukončí čekání na response z SQL Serveru. Typicky:
- ADO.NET
SqlCommand.CommandTimeoutdefault 30 s - ODBC
SQL_ATTR_QUERY_TIMEOUT - SSMS Options → Execution → Query timeout
Server dotaz často zpracovává dál; jen klient kapituloval. Cíl není navyšovat timeout na 300 s, ale zkrátit dotaz na < 2 s.
2 · Syndrom „SELECT *“: proč je to tichý zabiják
2.1 Přetékající kolony
Hvězdička vytáhne všechny sloupce, i když aplikace potřebuje dva. Více dat → více I/O → pomalejší síť → větší paměť → déle se zamyká.
2.2 Columnstore penalty
Columnstore index je super pro analytiku, ale SELECT * na 100 sloupců vyčistí delta rowgroup a zahodí kompresi.
2.3 Fix
SELECT OrderID, OrderDate, TotalPrice
FROM dbo.Orders
WHERE CustomerID = 123;
Ušetříš I/O i síť. Připni COVERING index a plán změní Key Lookup → Index Seek.
3 · TOP N – lék, ale i past
SELECT TOP 1000 * FROM AuditLog ORDER BY TimeStamp DESC; vypadá nevinně, ale:
- SQL Server nejčastěji sortuje všechno a pak vezme 1000 – pokud není index na
TimeStamp DESC. - Chybí
OFFSET 0 FETCH NEXT→ nelze použít pager friendly plan.
3.1 Správný pattern
CREATE INDEX IX_Audit_TimeStamp_DESC ON AuditLog(TimeStamp DESC) INCLUDE (UserID, Action);
SELECT TOP 1000 UserID, Action, TimeStamp
FROM dbo.AuditLog
ORDER BY TimeStamp DESC;
4 · Index seek vs. scan: B‑tree detektivka
Seek = klíč přímo k listům, Scan = čtení celé tabulky.
- Fragmentace > 30 % → méně stránek v cache, seek se mění na range scan.
- Kompozitní index: pořadí sloupců nejselektivnější → méně selektivní.
- Filtered index na
Status='Active'zmenší velikost o 80 %.
5 · Statistiky & kardinalita: věštírna plánovače
Plánovač dotazu předem odhaduje, kolik řádků se vrátí. Špatný odhad = špatný plán.
UPDATE STATISTICS dbo.Orders WITH FULLSCAN;
ALTER DATABASE CURRENT SET AUTO_UPDATE_STATISTICS ON;
Po velké dávce INSERT/DELETE raději sp_updatestats.
6 · Plan Cache: sniffing, recompile, fix
6.1 Parameter Sniffing
První provedení uloží plán vhodný pro vstup @ID = 1; další dotaz s @ID = 100000 může špatně škálovat.
6.2 Řešení
OPTION(RECOMPILE)– dražší compile, ale jistota.OPTIMIZE FOR (@ID UNKNOWN)– neutrální odhad.sp_create_plan_guide– přinutit plán.
7 · Diagnostika: DMV, Query Store, Extended Events
7.1 DMV – Top timeout queries
SELECT TOP 15
qs.total_worker_time/1000 AS CPU_ms,
qs.total_logical_reads AS LReads,
qs.execution_count AS Runs,
qs.total_elapsed_time/qs.execution_count/1000 AS Avg_ms,
SUBSTRING(qt.text,1,200) AS QueryText
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
WHERE qs.last_elapsed_time/1000 > 5000 -- >5 s
ORDER BY Avg_ms DESC;
7.2 Query Store
Zapni ReadWrite. Report Regressed queries ukáže dotazy, které loni běžely 50 ms a teď 5 s.
7.3 Extended Events template
CREATE EVENT SESSION SlowQueries ON SERVER
ADD EVENT sqlserver.rpc_completed (
ACTION(sqlserver.sql_text)
WHERE duration > 5000000) -- >5 s
ADD TARGET package0.ring_buffer;
8 · Skripty na hledání TOP timeoutů
8.1 Chybové hlášky v SQL Error Logu
EXEC xp_readerrorlog 0, 1, N'timeout' ;
8.2 Počet timeoutů za den
SELECT CAST(ErrorLogTime AS date) AS Day,
COUNT(*) AS Timeouts
FROM sys.fn_get_audit_file('C:\MSSQL\Log\*.sqlaudit', NULL, NULL)
WHERE statement LIKE '%Timeout expired%'
GROUP BY CAST(ErrorLogTime AS date);
9 · Case study: 12 s → 120 ms bez nového HW
| Krok | Čas | Změna | Latency |
|---|---|---|---|
| Původní dotaz | — | SELECT * + sort | 12 000 ms |
Odebráno * | 10 min | Jen 4 sloupce | 5 800 ms |
Index na Date DESC | 15 min | COVERING | 680 ms |
| Updated stats | 3 min | FULLSCAN | 320 ms |
| Parameter Hint | 2 min | OPTION RECOMPILE | 120 ms |
Zero Kč, 50 min práce, 100× zrychlení.
10 · Desatero prevence timeoutu
- Nikdy
SELECT *v produkci. - Používej
TOP Ns INDEXEDORDER BY. - Update statistics po bulk loadu > 20 % dat.
- Pravidelný
IndexOptimize(rebuild/reorganize). - Zapni Query Store a monitoruj regrese.
- Loguj
Duration>5 sdo Central Logging. - Nastav
MAXDOPpro OLTP na <= 4. - Zákaz kartézských joinů (
NO_JOIN_PREDICATEalert). - Archivuj stará data do history tabulek/partition.
- Rehearsuj prod deploy na kopii DB se stejným objemem.
11 · FAQ: limit, deadlock, či latence?
- Navýšit timeout na klientu?
- Nouzovka pro reporty > 120 s; jinak řeš root cause.
- Timeout vs. deadlock?
- Deadlock = 1205, trvá ms; timeout hlásí 0 – 3642 a trvá desítky s.
- Pomůže více RAM?
- Až po optimalizaci dotazů; jinak jen dražší špatný plán.
12 · Závěr
Timeouty jsou SOS světlice databáze: volají o pomoc nadměrnému SELECT *, neaktuálním statistikám a lenivým indexům. Hvězdičku vyměň za seznam sloupců, přidej SEEK‑friendly index, aktualizuj statistiky a udej přísný TOP N. Pak můžeš sledovat, jak se graf průměrné latence noří z červené do zelené – ještě než se klient stihne nadechnout ke stížnosti.
Klíčová slova
query timeout, mssql slow query, select star, top clause, query optimize, index seek, exec plan, perf tuning, sql tips, t-sql best, database dev, dba




