it-swarm-eu.dev

Nejúčinnější způsob, jak získat rozsah dat

Jaký je nejúčinnější způsob načtení časových období s podobnou strukturou tabulky?

create table SomeDateTable
(
    id int identity(1, 1) not null,
    StartDate datetime not null,
    EndDate datetime not null
)
go

Řekněme, že chcete rozsah pro StartDate a EndDate. Jinými slovy, pokud StartDate spadne mezi @StartDateBegin a @StartDateEnd a EndDate spadá mezi @EndDateBegin a @EndDateEnd, pak něco udělej.

Vím, že existuje několik způsobů, jak to asi udělat, ale co je nejvíce doporučeno?

16
Thomas Stringer

Toto je obtížný problém obecně, ale existuje několik věcí, které můžeme udělat, aby optimalizátor mohl zvolit plán. Tento skript vytvoří tabulku s 10 000 řádky se známým pseudonáhodným rozdělením řádků pro ilustraci:

CREATE TABLE dbo.SomeDateTable
(
    Id          INTEGER IDENTITY(1, 1) PRIMARY KEY NOT NULL,
    StartDate   DATETIME NOT NULL,
    EndDate     DATETIME NOT NULL
);
GO
SET STATISTICS XML OFF
SET NOCOUNT ON;
DECLARE
    @i  INTEGER = 1,
    @s  FLOAT = Rand(20120104),
    @e  FLOAT = Rand();

WHILE @i <= 10000
BEGIN
    INSERT dbo.SomeDateTable
        (
        StartDate, 
        EndDate
        )
    VALUES
        (
        DATEADD(DAY, @s * 365, {d '2009-01-01'}),
        DATEADD(DAY, @s * 365 + @e * 14, {d '2009-01-01'})
        )

    SELECT
        @s = Rand(),
        @e = Rand(),
        @i += 1
END

První otázkou je, jak tuto tabulku indexovat. Jednou z možností je poskytnout dva indexy ve sloupcích DATETIME, takže optimalizátor si může alespoň vybrat, zda hledat v StartDate nebo EndDate.

CREATE INDEX nc1 ON dbo.SomeDateTable (StartDate, EndDate)
CREATE INDEX nc2 ON dbo.SomeDateTable (EndDate, StartDate)

Nerovnosti v StartDate a EndDate přirozeně znamenají, že hledání v příkladu dotazu může podporovat pouze jeden sloupec, ale jde o to nejlepší, co můžeme udělat. Můžeme zvážit vytvoření druhého sloupce v každém indexu INCLUDE spíše než klíče, ale můžeme mít další dotazy, které mohou provést hledání rovnosti v hlavním sloupci a hledání nerovnosti ve druhém sloupci. Tímto způsobem také můžeme získat lepší statistiky. Tak jako tak...

DECLARE
    @StartDateBegin DATETIME = {d '2009-08-01'},
    @StartDateEnd DATETIME = {d '2009-10-15'},
    @EndDateBegin DATETIME = {d '2009-08-05'},
    @EndDateEnd DATETIME = {d '2009-10-22'}

SELECT
    COUNT_BIG(*)
FROM dbo.SomeDateTable AS sdt
WHERE
    sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
    AND sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd

Tento dotaz používá proměnné, takže obecně optimalizátor bude hádat o selektivitě a distribuci, což povede k odhadu kardinality 81 řádků . Ve skutečnosti dotaz vytváří 2076 řádků, což je nesrovnalost, která může být důležitá v komplexnějším příkladu.

Na SQL Server 2008 SP1 CU5 nebo novějším (nebo R2 RTM CU1) můžeme využít Optimalizace vkládání parametrů , abychom získali lepší odhady, jednoduše přidáním OPTION (RECOMPILE) k výše uvedenému dotazu SELECT. To způsobí kompilaci těsně před spuštěním dávky, což umožňuje serveru SQL „vidět“ skutečné hodnoty parametrů a optimalizovat pro ně. S touto změnou se odhad zlepšuje na 468 řádků (i když je třeba zkontrolovat runtime plán, abyste to viděli). Tento odhad je lepší než 81 řádků, ale stále ještě ne tak blízko. modelovací rozšíření povolená pomocí příznak trasování 2301 může v některých případech pomoci, ale ne s tímto dotazem.

Problém je v tom, že se řádky kvalifikované dvěma vyhledáváními rozsahu překrývají. Jedním ze zjednodušujících předpokladů učiněných ve složce nákladů a odhadu mohutnosti optimalizátoru je to, že predikáty jsou nezávislé (takže pokud oba mají selektivitu 50%, předpokládá se, že výsledek použití obou způsobil 50% 50% = 25% řádků) ). Pokud je tento druh korelace problémem, můžeme jej často obejít pomocí více sloupcových a/nebo filtrovaných statistik. Se dvěma rozsahy s neznámým počátečním a koncovým bodem se to stane nepraktickým. Zde se někdy musíme uchýlit k přepsání dotazu do podoby, ve které se vytvoří lepší odhad:

SELECT COUNT(*) FROM
(
    SELECT
        sdt.Id
    FROM dbo.SomeDateTable AS sdt
    WHERE 
        sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
    INTERSECT
    SELECT
        sdt.Id
    FROM dbo.SomeDateTable AS sdt 
    WHERE
        sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd
) AS intersected (id)
OPTION (RECOMPILE)

Tato forma náhodou vytvoří odhad runtime 2110 řádků (oproti 2076 skutečným). Pokud nemáte TF 2301, v tomto případě pokročilejší techniky modelování prokáží trik a vytvoří přesně stejný odhad jako dříve: 468 řádků.

Jeden den SQL Server může získat nativní podporu pro intervaly. Pokud k tomu dojde s dobrou statistickou podporou, vývojáři se mohou obávat plánů ladění dotazů, jako je tento, o něco méně.

29
Paul White 9

Nevím řešení, které je rychlé pro všechny distribuce dat, ale pokud jsou všechny vaše rozsahy krátké, můžeme je obvykle urychlit. Pokud jsou například rozsahy kratší než jeden den, namísto tohoto dotazu:

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt

můžeme přidat ještě jednu podmínku:

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt
    AND StartedAt >= '20101202'
    AND FinishedAt <= '20101204' ;

Výsledkem je, že místo prohledávání celé tabulky bude dotaz prohledávat pouze rozsah dvou dnů, což je rychlejší. Pokud mohou být rozsahy delší, můžeme je uložit jako sekvence kratších. Podrobnosti zde: Ladění dotazů SQL pomocí omezení

5
A-K