Preskočiť na obsah
Návod

Non-SARGable podmienky – funkcia na stĺpci, LIKE '%x', OR

Prečo index nepomôže, keď je stĺpec vo funkcii, výpočte alebo LIKE s % na začiatku: SARGable vs. non-SARGable podmienky, OR cez rôzne stĺpce, catch-all dotazy a computed column.

3 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

![Non-SARGable podmienky a ich prepisy](uploads/2026/11/plan-p6.svg "Stĺpec vo funkcii znemožní seek – výpočet patrí na stranu hodnoty")

Index na stĺpci existuje, ale v pláne je **Index Scan / Clustered Index Scan** s *Predicate* obsahujúcim funkciu nad stĺpcom (`datepart(year, OrderDate)`, `isnull(Note, '')`), alebo seek s obrovským *Number of Rows Read*.

## Čo to znamená

**SARGable** (*Search ARGument able*) podmienka je taká, ktorú vie index použiť na **hľadanie**: stĺpec je sám na jednej strane porovnania a druhá strana je hodnota. Keď stĺpec „zabalíš“ do funkcie alebo výpočtu, SQL Server musí výraz vypočítať pre **každý riadok** – index sa dá len prečítať celý. Navyše odhad počtu riadkov býva horší (pre výraz nemá štatistiku).

### Do hĺbky

- Typické prípady: funkcia na stĺpci (`YEAR()`, `CONVERT()`, `ISNULL()`, `LEFT()`, `UPPER()`), aritmetika (`Amount * 1.2 > 1000`), [implicitná konverzia](script.php?id=88&s=implicitna-konverzia-convert-implicit-varchar-vs-nvarchar), `LIKE '%text'` (začiatok neznámy), skalárna UDF na stĺpci.
- Niektoré výnimky optimalizátor zvláda: `CAST(datetime_stĺpec AS date) = '20260115'` sa dá premeniť na seek cez rozsah; `LIKE 'abc%'` je seek.
- **OR** cez rôzne stĺpce: jeden index nestačí – výsledok je sken, alebo (ak existujú indexy na oba stĺpce) *index union* cez Concatenation. Prepis na `UNION ALL` / `UNION` dáva optimalizátoru jasné seeky.
- „Catch-all“ dotazy `WHERE (@p IS NULL OR col = @p)` – jeden plán musí fungovať pre všetky kombinácie → sken. Riešenie: `OPTION (RECOMPILE)`, dynamické SQL, v SQL Server 2025 OPPO ([parameter sniffing](script.php?id=87&s=parameter-sniffing-jeden-plan-pre-rozne-hodnoty-psp-2022-oppo-2025)).
- Ak výraz potrebuješ, **computed column** s indexom (persisted alebo nie) dá výrazu štatistiku aj index.

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

| V poriadku | Problém |
|---|---|
| funkcia na hodnote / parametri (`OrderDate >= DATEADD(DAY, -7, @d)`) | funkcia na stĺpci vo WHERE / JOIN veľkej tabuľky |
| malá tabuľka | sken napriek existujúcemu indexu |

## Ako si to vyskúšať

```sql
USE PlanLab;
SET STATISTICS IO ON;

SELECT COUNT(*) FROM dbo.Orders WHERE YEAR(OrderDate) = 2025;                               -- Index Scan
SELECT COUNT(*) FROM dbo.Orders WHERE OrderDate >= '20250101' AND OrderDate < '20260101';    -- Index Seek

SELECT COUNT(*) FROM dbo.Customers WHERE Email LIKE '%9@example.com';    -- sken
SELECT COUNT(*) FROM dbo.Customers WHERE Email LIKE 'zakaznik19%';      -- seek

SELECT OrderID FROM dbo.Orders WHERE CustomerID = 5 OR AccountCode = 'AC000123';           -- čo zvolí?
SELECT OrderID FROM dbo.Orders WHERE CustomerID = 5
UNION
SELECT OrderID FROM dbo.Orders WHERE AccountCode = 'AC000123';                              -- dva seeky

-- computed column pre výraz, ktorý potrebuješ
ALTER TABLE dbo.Orders ADD OrderYear AS YEAR(OrderDate);
CREATE INDEX IX_Lab_Orders_Year ON dbo.Orders (OrderYear);
SELECT COUNT(*) FROM dbo.Orders WHERE YEAR(OrderDate) = 2025;   -- optimalizátor použije index na computed column
DROP INDEX IX_Lab_Orders_Year ON dbo.Orders;
ALTER TABLE dbo.Orders DROP COLUMN OrderYear;
```

Spusti s **Include Actual Execution Plan** (Ctrl+M) a porovnaj typ operátora (Scan / Seek), *Predicate* a logical reads.

## Čo s tým

1. Prepíš podmienku tak, aby stĺpec ostal „holý“ – výpočet presuň na stranu hodnoty (rozsah dátumov namiesto YEAR, porovnanie bez ISNULL…).
2. OR cez rôzne stĺpce → UNION / UNION ALL alebo vhodné indexy.
3. Vyhľadávanie podľa konca textu → iný model (reverzný stĺpec s indexom, full-text), nie LIKE '%x'.
4. Výraz, bez ktorého sa nedá: computed column + index.

## Súvisiace

- [Implicitná konverzia](script.php?id=88&s=implicitna-konverzia-convert-implicit-varchar-vs-nvarchar)
- [Index Seek – seek vs. residual predicate](script.php?id=69&s=index-seek-seek-predicate-vs-residual-predicate)
- [Table Scan a Clustered Index Scan](script.php?id=68&s=table-scan-a-clustered-index-scan-citanie-celej-tabulky)
- [Parameter sniffing](script.php?id=87&s=parameter-sniffing-jeden-plan-pre-rozne-hodnoty-psp-2022-oppo-2025)
- [Chýbajúce indexy – návrhy optimalizátora](script.php?id=28&s=chybajuce-indexy-navrhy-optimalizatora)

## Zdroje

- [Microsoft Learn – Query Processing Architecture Guide](https://learn.microsoft.com/sql/relational-databases/query-processing-architecture-guide)
- [Paul White – SQLkiwi (články o optimalizátore a operátoroch)](https://www.sql.kiwi/)
- [Brent Ozar – blog a nástroje First Responder Kit](https://www.brentozar.com/)
- [Grant Fritchey – SQL Server Execution Plans, 3. vydanie (Redgate, e-kniha zadarmo)](https://www.red-gate.com/simple-talk/books/sql-server-execution-plans-third-edition/)

_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._