La maggior parte modo efficace per recuperare intervalli di date
-
16-10-2019 - |
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?
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