sql >> Database teknologi >  >> RDS >> Sqlserver

Betinget aggregeringsydelse

Kort resumé

  • Udførelsen af ​​underforespørgselsmetoden afhænger af datafordelingen.
  • Ydeevne af betinget aggregering afhænger ikke af datafordelingen.

Underforespørgselsmetoden kan være hurtigere eller langsommere end betinget aggregering, det afhænger af datafordelingen.

Naturligvis, hvis tabellen har et passende indeks, vil underforespørgsler sandsynligvis drage fordel af det, fordi indeks ville tillade kun at scanne den relevante del af tabellen i stedet for den fulde scanning. At have et passende indeks er usandsynligt, at det vil gavne den betingede aggregeringsmetode væsentligt, fordi det alligevel vil scanne hele indekset. Den eneste fordel ville være, hvis indekset er smallere end tabellen, og motoren skulle læse færre sider ind i hukommelsen.

Når du ved dette, kan du bestemme, hvilken metode du skal vælge.

Første test

Jeg lavede et større testbord med 5M rækker. Der var ingen indekser på bordet. Jeg målte IO- og CPU-statistikken ved hjælp af SQL Sentry Plan Explorer. Jeg brugte SQL Server 2014 SP1-CU7 (12.0.4459.0) Express 64-bit til disse tests.

Faktisk opførte dine oprindelige forespørgsler, som du beskrev, dvs. underforespørgsler var hurtigere, selvom aflæsningerne var 3 gange højere.

Efter få forsøg på en tabel uden et indeks omskrev jeg dit betingede aggregat og tilføjede variabler til at holde værdien af ​​DATEADD udtryk.

Samlet blev tiden betydeligt hurtigere.

Så erstattede jeg SUM med COUNT og det blev en lille smule hurtigere igen.

Betinget aggregering blev trods alt stort set lige så hurtigt som underforespørgsler.

Opvarm cachen (CPU=375)

SELECT -- varm cache COUNT(*) AS all_cntFROM LogTableOPTION (RECOMPILE); 

Underforespørgsler (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_cntOPTION (RECOMPILE); 

Original betinget aggregering (CPU=1641)

SELECT -- betinget 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_cntFROM LogTableOPTION (RECOMPILE); 

Betinget aggregering med variabler (CPU=1078)

DECLARE @VarYear1 datetime =DATEADD(year,-1,GETDATE());DECLARE @VarYear10 datetime =DATEADD(year,-10,GETDATE());SELECT -- betingede variable 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_cntFROM LogTableOPTION); 

Betinget aggregering med variabler og COUNT i stedet for SUM (CPU=1062)

SELECT -- betinget variabel, tæl, ikke 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_cntFROM LogTableOPTION (RECOMPILE);

Baseret på disse resultater er mit gæt, at CASE påberåbt DATEADD for hver række, mens WHERE var smart nok til at regne det ud en gang. Plus COUNT er en lille smule mere effektiv end SUM .

I sidste ende er betinget aggregering kun lidt langsommere end underforespørgsler (1062 vs 1031), måske fordi Hvor er en smule mere effektiv end CASE i sig selv, og desuden WHERE filtrerer en del rækker fra, så COUNT skal behandle færre rækker.

I praksis ville jeg bruge betinget aggregering, fordi jeg tror, ​​at antallet af læsninger er vigtigere. Hvis dit bord er lille til at passe og forblive i bufferpuljen, vil enhver forespørgsel være hurtig for slutbrugeren. Men hvis tabellen er større end tilgængelig hukommelse, så forventer jeg, at læsning fra disk vil bremse underforespørgsler betydeligt.

Anden test

På den anden side er det også vigtigt at filtrere rækkerne ud så tidligt som muligt.

Her er en lille variation af testen, som demonstrerer det. Her sætter jeg tærsklen til GETDATE() + 100 år for at sikre, at ingen rækker opfylder filterkriterierne.

Opvarm cachen (CPU=344)

SELECT -- varm cache COUNT(*) AS all_cntFROM LogTableOPTION (RECOMPILE); 

Underforespørgsler (CPU=500)

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

Original betinget aggregering (CPU=937)

VÆLG -- betinget original COUNT(*) AS all_cnt, SUM(CASE WHEN datesent> DATEADD(year,100,GETDATE()) THEN 1 ELSE 0 END) AS last_ten_year_cntFROM LogTableOPTION (RECOMPILE); 

Betinget aggregering med variabler (CPU=750)

DECLARE @VarYear100 datetime =DATEADD(year,100,GETDATE());SELECT -- betingede variabler COUNT(*) AS all_cnt, SUM(CASE WHEN datesent> @VarYear100 THEN 1 ELSE 0 END) AS last_ten_year_cnt LogTableOPTION (GENKOMPIL); 

Betinget aggregering med variabler og COUNT i stedet for SUM (CPU=750)

SELECT -- betinget variabel, tæl, ikke sum COUNT(*) AS all_cnt, COUNT(CASE WHEN datesent> @VarYear100 THEN 1 ELSE NULL END) AS last_ten_year_cntFROM LogTableOPTION (RECOMPILE); 

Nedenfor er en plan med underforespørgsler. Du kan se, at 0 rækker gik ind i Stream Aggregate i den anden underforespørgsel, alle blev filtreret fra ved tabelscanningstrinnet.

Som et resultat er underforespørgsler igen hurtigere.

Tredje test

Her ændrede jeg filtreringskriterierne for den forrige test:alle > blev erstattet med < . Som følge heraf vil den betingede COUNT talte alle rækker i stedet for ingen. Overraskelse, overraskelse! Betinget aggregeringsforespørgsel tog samme 750 ms, mens underforespørgsler blev 813 i stedet for 500.

Her er planen for underforespørgsler:

Kan du give mig et eksempel, hvor betinget aggregering især klarer sig bedre end underforespørgselsløsningen?

Her er det. Underforespørgselsmetodens ydeevne afhænger af datafordelingen. Ydelse af betinget aggregering afhænger ikke af datafordelingen.

Underforespørgselsmetoden kan være hurtigere eller langsommere end betinget aggregering, det afhænger af datafordelingen.

Når du ved dette, kan du bestemme, hvilken metode du skal vælge.

Bonusdetaljer

Hvis du holder musen over Tabelscanning operatør kan du se Faktisk datastørrelse i forskellige varianter.

  1. Simpel COUNT(*) :

  1. Betinget aggregering:

  1. Underforespørgsel i test 2:

  1. Underforespørgsel i test 3:

Nu bliver det klart, at forskellen i ydeevne sandsynligvis skyldes forskellen i mængden af ​​data, der flyder gennem planen.

I tilfælde af simpel COUNT(*) der er ingen Outputliste (ingen kolonneværdier er nødvendige), og datastørrelsen er mindst (43MB).

I tilfælde af betinget aggregering ændres dette beløb ikke mellem test 2 og 3, det er altid 72 MB. Outputliste har én kolonne datesent .

I tilfælde af underforespørgsler, gør dette beløb ændres afhængigt af datafordelingen.



  1. Forespørgsel timeout fra webapp, men kører fint fra management studio

  2. MMO-spil og databasedesign

  3. Sådan opretter du forbindelse til SQL-serverdatabase fra en Windows 10 UWP-app

  4. Android SQLite Journal-adfærd ændret?