sql >> Databáze >  >> RDS >> Sqlserver

Výkon podmíněné agregace

Krátké shrnutí

  • Výkon metody dílčích dotazů závisí na distribuci dat.
  • Výkon podmíněné agregace nezávisí na distribuci dat.

Metoda poddotazů může být rychlejší nebo pomalejší než podmíněná agregace, záleží na distribuci dat.

Přirozeně, pokud má tabulka vhodný index, pak z něj pravděpodobně budou těžit poddotazy, protože index by umožnil skenovat pouze relevantní část tabulky namísto úplného skenování. Je nepravděpodobné, že by vhodný index významně prospěl metodě podmíněné agregace, protože stejně prohledá celý index. Jedinou výhodou by bylo, kdyby byl index užší než tabulka a engine by musel číst méně stránek do paměti.

S tímto vědomím se můžete rozhodnout, kterou metodu zvolíte.

První test

Udělal jsem větší testovací stůl s 5M řádky. Na stole nebyly žádné indexy. Měřil jsem statistiky IO a CPU pomocí SQL Sentry Plan Explorer. Pro tyto testy jsem použil SQL Server 2014 SP1-CU7 (12.0.4459.0) Express 64bitový.

Vaše původní dotazy se skutečně chovaly tak, jak jste popsali, tj. poddotazy byly rychlejší, i když čtení bylo 3krát vyšší.

Po několika pokusech na tabulce bez indexu jsem přepsal váš podmíněný agregát a přidal proměnné tak, aby obsahovaly hodnotu DATEADD výrazy.

Celkový čas se výrazně zrychlil.

Potom jsem nahradil SUM s COUNT a bylo to zase o něco rychlejší.

Koneckonců, podmíněná agregace byla téměř stejně rychlá jako dílčí dotazy.

Zahřejte mezipaměť (CPU=375)

SELECT -- warm cache
    COUNT(*) AS all_cnt
FROM LogTable
OPTION (RECOMPILE);

Poddotazy (CPU=1031)

SELECT -- subqueries
(
    SELECT count(*) FROM LogTable 
) all_cnt, 
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,-1,GETDATE())
) last_year_cnt,
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,-10,GETDATE())
) last_ten_year_cnt
OPTION (RECOMPILE);

Původní podmíněná agregace (CPU=1641)

SELECT -- conditional original
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,-1,GETDATE())
             THEN 1 ELSE 0 END) AS last_year_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,-10,GETDATE())
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Podmíněná agregace s proměnnými (CPU=1078)

DECLARE @VarYear1 datetime = DATEADD(year,-1,GETDATE());
DECLARE @VarYear10 datetime = DATEADD(year,-10,GETDATE());

SELECT -- conditional variables
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > @VarYear1
             THEN 1 ELSE 0 END) AS last_year_cnt,
    SUM(CASE WHEN datesent > @VarYear10
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Podmíněná agregace s proměnnými a COUNT namísto SUM (CPU=1062)

SELECT -- conditional variable, count, not sum
    COUNT(*) AS all_cnt,
    COUNT(CASE WHEN datesent > @VarYear1
             THEN 1 ELSE NULL END) AS last_year_cnt,
    COUNT(CASE WHEN datesent > @VarYear10
             THEN 1 ELSE NULL END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Na základě těchto výsledků odhaduji, že CASE vyvoláno DATEADD pro každý řádek, zatímco WHERE byl dost chytrý, aby to jednou spočítal. Plus COUNT je o něco málo efektivnější než SUM .

Podmíněná agregace je nakonec jen o něco pomalejší než dílčí dotazy (1062 vs 1031), možná proto, že WHERE je o něco efektivnější než CASE samo o sobě a kromě toho WHERE odfiltruje poměrně dost řádků, takže COUNT musí zpracovat méně řádků.

V praxi bych použil podmíněnou agregaci, protože si myslím, že počet přečtení je důležitější. Pokud je vaše tabulka malá, aby se vešla a zůstala ve fondu vyrovnávacích pamětí, bude jakýkoli dotaz pro koncového uživatele rychlý. Pokud je však tabulka větší než dostupná paměť, pak očekávám, že čtení z disku výrazně zpomalí poddotazy.

Druhý test

Na druhou stranu je také důležité odfiltrovat řádky co nejdříve.

Zde je malá obměna testu, která to demonstruje. Zde jsem nastavil práh na GETDATE() + 100 let, abych se ujistil, že žádné řádky nesplňují kritéria filtru.

Zahřejte mezipaměť (CPU=344)

SELECT -- warm cache
    COUNT(*) AS all_cnt
FROM LogTable
OPTION (RECOMPILE);

Poddotazy (CPU=500)

SELECT -- subqueries
(
    SELECT count(*) FROM LogTable 
) all_cnt, 
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,100,GETDATE())
) last_year_cnt
OPTION (RECOMPILE);

Původní podmíněná agregace (CPU=937)

SELECT -- conditional original
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,100,GETDATE())
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Podmíněná agregace s proměnnými (CPU=750)

DECLARE @VarYear100 datetime = DATEADD(year,100,GETDATE());

SELECT -- conditional variables
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > @VarYear100
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Podmíněná agregace s proměnnými a COUNT namísto SUM (CPU=750)

SELECT -- conditional variable, count, not sum
    COUNT(*) AS all_cnt,
    COUNT(CASE WHEN datesent > @VarYear100
             THEN 1 ELSE NULL END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Níže je plán s dílčími dotazy. Můžete vidět, že v druhém dílčím dotazu do Stream Aggregate šlo 0 řádků, všechny byly odfiltrovány v kroku Skenování tabulky.

Díky tomu jsou poddotazy opět rychlejší.

Třetí test

Zde jsem změnil kritéria filtrování předchozího testu:vše > byly nahrazeny < . Výsledkem je podmíněné COUNT počítal všechny řádky místo žádné. Překvapení, překvapení! Podmíněný agregační dotaz trval stejně 750 ms, zatímco poddotazy se staly 813 namísto 500.

Zde je plán pro dílčí dotazy:

Mohl byste mi uvést příklad, kde podmíněná agregace výrazně převyšuje výkon řešení poddotazů?

Tady to je. Výkon metody poddotazů závisí na distribuci dat. Výkon podmíněné agregace nezávisí na distribuci dat.

Metoda poddotazů může být rychlejší nebo pomalejší než podmíněná agregace, záleží na distribuci dat.

S tímto vědomím se můžete rozhodnout, kterou metodu zvolíte.

Podrobnosti o bonusu

Pokud najedete myší na Table Scan operátora můžete vidět Actual Data Size v různých variantách.

  1. Jednoduché COUNT(*) :

  1. Podmíněná agregace:

  1. Poddotaz v testu 2:

  1. Poddotaz v testu 3:

Nyní je jasné, že rozdíl ve výkonu je pravděpodobně způsoben rozdílem v množství dat, které plánem protékají.

V případě jednoduchého COUNT(*) neexistuje žádný Output list (nejsou potřeba žádné hodnoty sloupců) a velikost dat je nejmenší (43 MB).

V případě podmíněné agregace se tato částka mezi testy 2 a 3 nemění, vždy je 72 MB. Output list má jeden sloupec datesent .

V případě dílčích dotazů tato částka dělá měnit v závislosti na distribuci dat.



  1. Jak mohu echo ID zdroje #6 z odpovědi MySql v PHP?

  2. Vytvořte nového uživatele Oracle a udělte oprávnění:Syntaxe a příklady

  3. Nejlepší způsob, jak získat PK Guid vloženého řádku

  4. Dynamické maskování dat v SQL Server pro pokročilé uživatele