Dotazování minimální hodnota v SQL Server je mnohem delší než dotazování všechny řádky

hlasů
3

Jsem v současné době konfrontováni s podivným chováním v mé databáze, když jsem dotazování minimální ID pro určitý den v tabulce obsahuje asi sto miliónů řádků. Dotaz je docela jednoduchý:

SELECT MIN(Id) FROM Connection WITH(NOLOCK) WHERE DateConnection = '2012-06-26'

Tento dotaz Nevers konec, alespoň já nechat běžet několik hodin. Sloupec DateConnection není index ani zahrnuta v jednom. Takže bych pochopit, že tento dotaz může trvat docela dost. Ale snažil jsem následující dotaz, který probíhá v několika sekundách:

SELECT Id FROM Connection WITH(NOLOCK) WHERE DateConnection = '2012-06-26'

Vrátí 300K řádky.

Můj stůl je definován takto:

CREATE TABLE [dbo].[Connection](  
    [Id] [bigint] IDENTITY(1,1) NOT NULL,  
    [DateConnection] [datetime] NOT NULL,  
    [TimeConnection] [time](7) NOT NULL,  
    [Hour]  AS (datepart(hour,[TimeConnection])) PERSISTED NOT NULL,  
    CONSTRAINT [PK_Connection] PRIMARY KEY CLUSTERED   
    (  
        [Hour] ASC,  
        [Id] ASC  
    )  
)

A má následující index:

CREATE UNIQUE NONCLUSTERED INDEX [IX_Connection_Id] ON [dbo].[Connection]  
(  
    [Id] ASC  
)ON [PRIMARY]

Jeden řešení najdu pomocí této podivné chování se pomocí následující kód. Ale zdá se mi trochu těžké pro takový jednoduchý dotaz.

create table #TempId
(
    [Id] bigint
)
go

insert into #TempId
select id from partitionned_connection with(nolock) where dateconnection = '2012-06-26'

declare @displayId bigint
select @displayId = min(Id) from #CoIdTest

print @displayId 
go

drop table #TempId
go

Má někdo byl konfrontován s tímto chováním a co je jeho příčinou? Je minimální agregát skenování celou tabulku? A pokud tomu tak je, proč je jednoduchý select ne?

Položena 25/07/2012 v 07:02
zdroj uživatelem
V jiných jazycích...                            


4 odpovědí

hlasů
-1

Je pochopitelné, že zjištění minimální trvá déle, než jít přes všechny záznamy. Nalezení minimálně o netříděného struktury trvá mnohem déle, než jednou najet ji (netříděné protože MIN () nevyužije sloupce identity). Co byste mohli dělat, protože používáte sloupec identity, je mít vnořené select, kde budete mít první záznam ze sady záznamů s konkrétním datem.

Odpovězeno 25/07/2012 v 07:29
zdroj uživatelem

hlasů
-2

Skenování NC index je problém ve vás case.It používá jedinečný seskupený skenování non index a pak pro každý řádek, který je sto miliónů řádků bude procházet seskupený index, a tudíž způsobuje miliony io to (obvykle říkají vaše index hieght 4 pak by to mohlo způsobit 100million * 4 Io index + scan stránky nonclustered index listové) .Optimizer musí si vybrali tento index, aby se zabránilo Strem agregát seznamte se s minimum.To najít minimum jsou tři hlavní techniky, jeden používá index na sloupec, pro který chceme min (to je efektivní, pokud existuje index i v tomto případě není vypočteno zapotřebí, jakmile se dostanete na řádek je vrácena), druhý by to mohlo použít hash agregát (ale to se obvykle stává, když máte skupinu o) a třetí je proud agregát tady to bude skenovat přes všechny řádky, které jsou oprávněné a udržet hodnotu min vždy a zpětný chod min, když jsou všechny řádky skenovány ..

Howvere, když dotaz, aniž by min použili seskupený skenování index, a proto je velmi jednoduché, jak to má číst menší počet stránek a tím i méně Io.

Nyní je otázkou, proč optimalizace zvedl index prověřování non clusterových index.I si jist, že je vyhnout se compuation zapojené do toku agregátu najít hodnotu min pomocí proudu agregát, ale v případě Thise nepoužívá proud agregát je mnohem nákladnější. To závisí na odhadu, takže myslím, že statistiky nejsou aktuální v tabulce.

Takže pěst řadě zkontrolujte, zda vaše statistiky jsou aľ date.When Byli statistiky byly aktualizovány naposledy?

Tak, aby se zabránilo issue.Do po 1. Nejprve aktualizaci statistiky tabulky a jsem si jist, že je třeba odstranit váš problém. 2. V případě, že nelze použít aktualizace statistiky nebo aktualizovat statistiky doesnt změnit plán a stále používá NC index skenování pak můžete vynutit seskupený skenování indexu tak, že používá méně následuje proud agregátu dostat min hodnotu Io.

Odpovězeno 26/07/2012 v 19:33
zdroj uživatelem

hlasů
1

I když to by mohlo být moudré, aby problém vyřešit způsobem, který nevyžaduje indexu rady, rychlé řešení, je toto:

SELECT MIN(Id) FROM Connection WITH(NOLOCK, INDEX(PK_Connection)) WHERE DateConnection = '2012-06-26'

To vynutí prohledání tabulky.

Případně zkusit i když pravděpodobně produkuje stejný problém:

select top 1 Id
from Connection
WHERE DateConnection = '2012-06-26'
order by Id
Odpovězeno 26/07/2012 v 20:46
zdroj uživatelem

hlasů
5

Hlavní příčinou tohoto problému je nezúčastněných nonclustered index, v kombinaci se statistickým omezením Martin Smith poukazuje (viz jeho odpověď na jinou otázku, kde najdete další podrobnosti).

Váš stůl je rozdělen na [Hour]tomto duchu:

CREATE PARTITION FUNCTION PF (integer)
AS RANGE RIGHT
FOR VALUES (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23);

CREATE PARTITION SCHEME PS
AS PARTITION PF ALL TO ([PRIMARY]);

-- Partitioned
CREATE TABLE dbo.Connection
(
    Id              bigint IDENTITY(1,1) NOT NULL,
    DateConnection  datetime NOT NULL,
    TimeConnection  time(7) NOT NULL,
    [Hour]  AS (DATEPART(HOUR, TimeConnection)) PERSISTED NOT NULL,

    CONSTRAINT [PK_Connection]
    PRIMARY KEY CLUSTERED
    (  
        [Hour] ASC,  
        [Id] ASC  
    )
    ON PS ([Hour])
);

-- Not partitioned
CREATE UNIQUE NONCLUSTERED INDEX [IX_Connection_Id]
ON dbo.Connection
(  
    Id ASC
)ON [PRIMARY];

-- Pretend there are lots of rows
UPDATE STATISTICS dbo.Connection WITH ROWCOUNT = 200000000, PAGECOUNT = 4000000;

Dotaz a realizace plánu jsou následující:

SELECT 
    MinID = MIN(c.Id)
FROM dbo.Connection AS c WITH (READUNCOMMITTED) 
WHERE
    c.DateConnection = '2012-06-26';

vybrán plán

Optimalizátor využívá indexu (objednané o Id) pro transformaci MINagregátu do A TOP (1)- protože je minimální hodnota bude ze své podstaty jako první hodnota se setkal v objednaném proudu. (Je-li neclusterovaný index byl také rozdělen, optimalizátor by nevybral této strategie, protože požadované uspořádání by byla ztracena).

Mírný komplikací je, že musíme také použít predikát v WHEREklauzule, který vyžaduje vyhledávání na základní tabulky načíst DateConnectionhodnotu. Statistická omezení Martin zmiňuje vysvětluje, proč je optimalizace odhaduje, že bude potřebovat pouze ke kontrole 119 řádků z objednané index než najít jeden s DateConnectionhodnotou, která bude odpovídat WHERE clause. Skryté korelace DateConnectiona Idhodnoty znamená, že tento odhad je velmi dlouhá cesta pryč.

V případě, že Vás zajímá je Compute skalární vypočítává které partition provést vyhledávací klíč do. Pro každý řádek z nonclustered indexu, se vypočte výraz, jako je [PtnId1000] = Scalar Operator(RangePartitionNew([dbo].[Connection].[Hour] as [c].[Hour],(1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23))), a tato se použije jako hlavní klíč vyhledávání hledat. Tam je prefetching (čtení napřed) na vnořené smyčky spojit, ale to musí být nařízeno prefetch zachovat třídění požadované TOP (1)optimalizace.

Řešení

Můžeme se vyhnout statistické omezení (bez použití dotazu rady) nalezením minimální Idpro každou Hourhodnotu, a pak brát minimum minima za hodinu:

-- Global minimum
SELECT 
    MinID = MIN(PerHour.MinId)
FROM 
(
    -- Local minimums (for each distinct hour value)
    SELECT 
        MinID = MIN(c.Id)
    FROM dbo.Connection AS c WITH(READUNCOMMITTED) 
    WHERE
        c.DateConnection = '2012-06-26' 
    GROUP BY
        c.[Hour]
) AS PerHour;

Plán realizace je:

Serial plán

Je-li povoleno paralelismus, uvidíte plán víc jako následující, který využívá paralelní index skenování a multi-threaded proud agregáty vyrábět výsledek ještě rychleji:

paralelní plán

Odpovězeno 16/12/2012 v 04:30
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more