Domanda

Qual è il modo più efficace per recuperare gli intervalli di date con una struttura di tabella come questa?

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

dici che vuoi una gamma sia per StartDate e EndDate. Quindi, in altre parole, se StartDate cade tra @StartDateBegin e @StartDateEnd, e EndDate cade tra @EndDateBegin e @EndDateEnd, poi fare qualcosa.

So che ci sono alcuni modi per andare su questo, probabilmente, ma qual è il più consigliato?

È stato utile?

Soluzione

Questo è un problema difficile da risolvere, in generale, ma ci sono un paio di cose che possiamo fare per aiutare l'ottimizzatore scegliere un piano. Questo script crea una tabella con 10.000 righe con una nota di distribuzione pseudo-casuale di righe per illustrare:

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

La prima domanda è come indice di questa tabella. Una possibilità è quella di fornire due indici sulle colonne DATETIME, in modo da l'ottimizzatore può almeno decidere se cercare su StartDate o EndDate.

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

Naturalmente, le disuguaglianze sia su StartDate e EndDate media che solo una colonna in ogni indice può supportare un cercano nella query di esempio, ma questo è circa il meglio che possiamo fare. Potremmo considerare la seconda colonna in ogni indice un INCLUDE piuttosto che una chiave, ma potremmo avere altre domande che possono eseguire un'uguaglianza cercare la colonna principale ed una disuguaglianza cercare sulla seconda colonna. Inoltre, possiamo ottenere migliori statistiche in questo modo. Comunque ...

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

Queste variabili usi query, quindi in generale l'ottimizzatore indovinare selettività e distribuzione, causando una stima cardinalità intuito di 81 righe . Infatti, l'interrogazione produce 2076 righe, una discrepanza che potrebbe essere importante in un esempio più complesso.

In SQL Server 2008 SP1 o versione successiva CU5 (o R2 RTM CU1) siamo in grado di sfruttare il Parametro Embedding Ottimizzazione per ottenere stime migliori, semplicemente aggiungendo OPTION (RECOMPILE) alla query SELECT sopra. Questo fa sì che una compilation poco prima che le esegue in batch, che consente a SQL Server di 'vedere' i valori dei parametri e ottimizzare vero per quelli. Con questa modifica, la stima migliora a 468 righe (se si ha bisogno di verificare il piano di runtime di vedere questo). Questa stima è migliore di 81 righe, ma non così vicino. Le estensioni di modellazione abilitati da flag di traccia 2301 può aiutare in alcuni casi, ma non con questa query.

Il problema è dove le righe qualificati da due ricerche gamma si sovrappongono. Una delle ipotesi semplificative realizzati in componente costing e cardinalità stima del ottimizzatore è che i predicati sono indipendenti (quindi se entrambi hanno una selettività del 50%, il risultato dell'applicazione di entrambi si presume qualificare 50% al 50% = 25% delle righe ). Quando questo tipo di correlazione è un problema, spesso possiamo lavorare intorno ad esso con il multi-colonna e / o statistiche filtrate. Con due gamme con punti di inizio e fine sconosciuti, questo diventa impraticabile. Questo è dove abbiamo talvolta ricorrere a riscrivere la query per una forma che succede a produrre una stima migliore:

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)

Questo modulo accade per produrre una stima runtime di 2110 righe (rispetto 2076 effettivo). A meno che non si dispone di TF 2301 in poi, nel qual caso le tecniche di modellazione più avanzate vedere attraverso il trucco e producono esattamente la stessa stima di prima:. 468 righe

Un giorno di SQL Server potrebbe ottenere il supporto nativo per gli intervalli. Se questo viene fornito con un buon supporto statistico, gli sviluppatori potrebbero temere di tuning piani di query come questo un po 'meno.

Altri suggerimenti

Non so una soluzione che è veloce per tutte le distribuzioni di dati, ma se tutte le gamme sono brevi, noi di solito può accelerarlo. Se, per esempio, gli intervalli sono più brevi di un giorno, invece di questa query:

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

siamo in grado di aggiungere un altro condizione:

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

Di conseguenza, invece di scansione l'intera tabella la query scansione gamma solo due giorni, che è più veloce. Se intervalli possono essere più lungo, possiamo memorizzare come sequenze di quelle più corte. Dettagli qui: sintonia query SQL con l'aiuto dei Vincoli

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top