Questo è un problema difficile da risolvere in generale, ma ci sono un paio di cose che possiamo fare per aiutare l'ottimizzatore a scegliere un piano. Questo script crea una tabella con 10.000 righe con una distribuzione pseudo-casuale nota 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 indicizzare questa tabella. Un'opzione è fornire due indici sulle DATETIME
colonne, in modo che l'ottimizzatore possa almeno scegliere se cercare StartDate
o EndDate
.
CREATE INDEX nc1 ON dbo.SomeDateTable (StartDate, EndDate)
CREATE INDEX nc2 ON dbo.SomeDateTable (EndDate, StartDate)
Naturalmente, le disuguaglianze su entrambi StartDate
e EndDate
significano che solo una colonna in ciascun indice può supportare una ricerca nella query di esempio, ma si tratta del meglio che possiamo fare. Potremmo considerare di rendere la seconda colonna in ciascun indice INCLUDE
una chiave anziché una chiave, ma potremmo avere altre query che possono eseguire una ricerca di uguaglianza sulla colonna principale e una ricerca di disuguaglianza sulla seconda colonna. Inoltre, possiamo ottenere statistiche migliori 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
Questa query utilizza variabili, quindi in generale l'ottimizzatore indovina la selettività e la distribuzione, ottenendo una stima della cardinalità indovinata di 81 righe . In effetti, la query produce 2076 righe, una discrepanza che potrebbe essere importante in un esempio più complesso.
Su SQL Server 2008 SP1 CU5 o versione successiva (o R2 RTM CU1) possiamo sfruttare l' ottimizzazione dell'incorporamento dei parametri per ottenere stime migliori, semplicemente aggiungendoOPTION (RECOMPILE)
alla SELECT
query sopra. Ciò provoca una compilazione appena prima dell'esecuzione del batch, consentendo a SQL Server di "vedere" i valori dei parametri reali e di ottimizzarli. Con questa modifica, la stima migliora a 468 righe (sebbene sia necessario controllare il piano di runtime per vederlo). Questa stima è migliore di 81 righe, ma non è ancora così vicina. Le estensioni di modellazione abilitate da flag di traccia 2301 possono aiutare in alcuni casi, ma non con questa query.
Il problema è che le righe qualificate dalle ricerche di due intervalli si sovrappongono. Una delle ipotesi semplificative fatte nella componente di stima dei costi e della cardinalità dell'ottimizzatore è che i predicati sono indipendenti (quindi se entrambi hanno una selettività del 50%, si presume che il risultato dell'applicazione di entrambi si qualifichi il 50% del 50% = 25% delle righe ). Laddove questo tipo di correlazione è un problema, possiamo spesso aggirare il problema con statistiche multi-colonna e / o filtrate. Con due intervalli con punti di inizio e fine sconosciuti, ciò diventa impraticabile. È qui che a volte dobbiamo ricorrere alla riscrittura della query in un modulo che produce 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 produce una stima di runtime di 2110 righe (rispetto al 2076 effettivo). A meno che tu non abbia TF 2301 acceso, nel qual caso le tecniche di modellazione più avanzate vedono attraverso il trucco e producono esattamente la stessa stima di prima: 468 righe.
Un giorno SQL Server potrebbe ottenere il supporto nativo per gli intervalli. Se questo viene fornito con un buon supporto statistico, gli sviluppatori potrebbero temere un po 'meno l'ottimizzazione dei piani di query come questa.