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