Preskočiť na obsah
Návod

Hash Match – hash join a hash aggregate (build a probe)

Hash Match v pláne: build a probe vstup, hash tabuľka v pamäti, memory grant, blokujúca build fáza, Hash Warning (spill), Probe Residual, bitmap a hash agregácia.

4 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

![Hash Match: build vstup, hash tabuľka a probe vstup](uploads/2026/11/plan-o6.svg "Build postaví hash tabuľku v pamäti, probe ju prehľadáva – pamäť sa určí z odhadu")

V SSMS: **Hash Match** – logická operácia povie, či ide o spojenie (*Inner Join*, *Left Outer Join*, *Right Semi Join*…) alebo agregáciu (*Aggregate*, *Flow Distinct*, *Partial Aggregate*). Spojenie má dva vstupy: **horný = build**, **spodný = probe**.

## Čo to znamená

**Hash join**: SQL Server prečíta celý horný (build) vstup a pre každý riadok uloží hodnotu kľúča spojenia do **hash tabuľky v pamäti**. Potom číta spodný (probe) vstup a pre každý riadok sa pozrie do hash tabuľky, či má partnera. Netreba zoradenie ani index – preto sa Hash Match objaví pri veľkých nezoradených vstupoch alebo keď chýba index.

**Hash aggregate**: rovnaký princíp pre GROUP BY / DISTINCT – každá skupina má v hash tabuľke svoj záznam s priebežným súčtom.

### Do hĺbky

- Hash tabuľka potrebuje **memory grant** (pamäť pridelenú pred štartom dotazu). Veľkosť sa určí z **odhadu** riadkov build vstupu. Pri podhodnotení sa časť hash tabuľky presunie do tempdb – **spill**, varovanie *Hash Warning* ([Spill do tempdb](script.php?id=90&s=spill-do-tempdb-sort-a-hash-warning-memory-grant-feedback)).
- Build fáza je **blokujúca** (stop-and-go): prvý riadok z Hash Match odíde až po prečítaní celého build vstupu.
- Optimalizátor dáva na build stranu **menší** vstup (podľa odhadu). Zlý odhad = veľký vstup na build strane = veľký grant alebo spill.
- **Hash Keys Build / Hash Keys Probe** – stĺpce spojenia. **Probe Residual** – ďalšia podmienka testovaná pri zhode hashu (vrátane overenia, že hodnoty sú naozaj rovnaké, nielen hash).
- V paralelných plánoch často vidíš **Bitmap** – filter z build strany, ktorý zahodí probe riadky skôr, než dôjdu k spojeniu.
- **Partial Aggregate** / *Flow Distinct* – varianty agregácie, ktoré posielajú riadky ďalej priebežne.

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

| V poriadku | Problém |
|---|---|
| veľké vstupy, reporty, agregácie | Hash Warning (spill) – pamäť nestačila |
| build vstup malý, probe veľký | build vstup obrovský kvôli zlému odhadu |
| dotaz potrebuje všetky riadky | OLTP dotaz, ktorý má vrátiť pár riadkov, ale číta celé tabuľky |
| memory grant sa využije | grant v GB, využitých pár MB ([Memory grant](script.php?id=91&s=memory-grant-kolko-pamate-dostal-dotaz-privela-alebo-primalo)) |

## Ako si to vyskúšať

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

-- 1) Hash join (Products ako build) + hash aggregate (GROUP BY Category)
SELECT p.Category, SUM(l.Qty * l.Price) AS trzba
FROM dbo.OrderLines AS l
JOIN dbo.Products AS p ON p.ProductID = l.ProductID
GROUP BY p.Category;

-- 2) Hash aggregate na veľkom vstupe bez indexu
SELECT ProductID, COUNT(*) AS pocet FROM dbo.OrderLines GROUP BY ProductID;

-- 3) Spill: obmedzený grant cez hint (len na ukážku; MAX_GRANT_PERCENT je od 2016)
SELECT o.CustomerID, COUNT(DISTINCT l.ProductID) AS produkty
FROM dbo.Orders AS o
JOIN dbo.OrderLines AS l ON l.OrderID = o.OrderID
GROUP BY o.CustomerID
OPTION (HASH JOIN, MAX_GRANT_PERCENT = 0.01);
```

Spusti s **Include Actual Execution Plan** (Ctrl+M). Pri Hash Match pozri *Hash Keys Build / Probe*, na SELECT *Memory Grant Info* a pri bode 3 varovanie (žltý trojuholník) so *spill level*.

## Čo s tým

1. Hash Match sám o sebe nie je chyba. Rieš ho, keď **spilluje**, keď má **obrovský grant** alebo keď OLTP dotaz zbytočne číta celé tabuľky.
2. Pri spille a zlých odhadoch oprav odhad ([štatistiky](script.php?id=84&s=zle-odhady-riadkov-odhad-vs-skutocnost-a-statistiky)), nie grant hintom.
3. Ak by stačil malý počet riadkov, index na stĺpce spojenia umožní [Nested Loops](script.php?id=71&s=nested-loops-vnorene-cykly-outer-a-inner-vstup) so seekom.
4. Pri veľkých analytických dotazoch zváž [columnstore a batch mode](script.php?id=81&s=columnstore-a-batch-mode-actual-execution-mode-segment-elimination) – hash operácie v batch mode sú výrazne efektívnejšie.

## Súvisiace

- [Spill do tempdb](script.php?id=90&s=spill-do-tempdb-sort-a-hash-warning-memory-grant-feedback)
- [Memory grant](script.php?id=91&s=memory-grant-kolko-pamate-dostal-dotaz-privela-alebo-primalo)
- [Stream Aggregate vs. Hash Aggregate](script.php?id=76&s=stream-aggregate-vs-hash-aggregate-group-by-v-plane)
- [Nested Loops](script.php?id=71&s=nested-loops-vnorene-cykly-outer-a-inner-vstup)
- [Memory grants – kto čaká na pamäť](script.php?id=19&s=memory-grants-kto-caka-na-pamat-resource-semaphore)
- [Čakanie RESOURCE_SEMAPHORE](waits.php#RESOURCE_SEMAPHORE)

## 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/)
- [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._