>non ci sono !!
beh.. allora andrà peggio sicuramente
>questa e' la tabella, tu come la indicizzeresti?
Innanzitutto riscriverei la query senza il between per comprendere tutti i valori (ho cambiato i nomi delle colonne NRPL-ShopOrder
e T&CC_Date, che non seguono gli standard):
SELECT
id
, SpA_Order
, Sud_Order
, NRPL_ShopOrder
, TCC_Date
, Prepack_IN_Date
, Shipping_Date
, Acceptance_Date
, Allocation_Date
FROM
OSM_Ord
WHERE
TCC_Date >= @DataInizio
AND TCC_Date <= @DataFine
poi, visto che il filtro è solo sulla data e che si tratta di una range search, metterei la PRIMARY KEY NONCLUSTERED e farei un indice CLUSTERED sul campo del range (TCC_Date). Successivamente controllerei se effettivamente mi servono tutti i campi della select. In caso negativo, indicherei solo quelli che mi servono.. Altra cosa da fare, se possibile, è dimensionare correttamente i tipi di dato, indicando il tipo più piccolo che soddisfa le esigenze del campo. I datetime, ad esempio, se non ti servono date inferiori al 1900 e se non ti serve la precisione dei secondi, sostituiscili con smalldatetime.. Ed infine, la normalizzazione.. se ci sono campi ridondanti che puoi portare in forma di relazione, fallo.. altrimenti la query impiegherà sempre molto tempo.
Eccoti un paio di esempi, impostando i flag STATISTICS IO e TIME ad ON (includono statistiche sull'input output e sui tempi di esecuzione):
caso 1: CLUSTERED SULLA CHIAVE PRIMARIA.
a) con filtro where che coinvolge tutta la tabella, 500000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 20938, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 610 ms, elapsed time = 16166 ms.
Quindi 20938 letture in 16 secondi. Il piano di esecuzione fa Clustered Index Scan.
b) con filtro ristretto, 300000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 20938, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 594 ms, elapsed time = 6224 ms.
Stesso numero di letture ma minor tempo, 6 secondi e stesso piano.
caso 2: CLUSTERED SUL CAMPO TCC_Date.
a) con filtro where che coinvolge tutta la tabella, 500000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 20938, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 594 ms, elapsed time = 15870 ms.
Quindi 20938 letture in 15 secondi. Il piano di esecuzione fa Clustered Index Seek.
b) con filtro ristretto (e qui ci risparmia sull'IO), 300000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 13144, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 484 ms, elapsed time = 5720 ms.
Numero di IO quasi dimezzato, piano con Clustered Index Seek.
Dopo aver fatto questi test possiamo concludere che se vai a filtrare per range più ristretti dell'intera tabella ti conviene fare il clustered sulla data, poichè nel primo caso, qualunque sia il range di date su cui ricerchiamo, le letture sono le medesime. Nel secondo invece l'IO si riduce notevolmente, fino a quasi dimezzarsi. Escludo infine la possibilità di lasciare la chiave come la indichi tu e mettere l'indice nonclustered sulla data, poichè i risultati sono gli stessi del primo caso. Nella tua problematica, con 500000 righe di prova, SQL preferisce infatti fare clustered index scan per ogni filtro, quindi il caso 1. Io utilizzerei il caso due, poichè non è detto che la tua select faccia sempre richiesta a tutti i record e quindi risparmierei sull'io.. Considera che se le due date sono vicine, i cicli di lettura si riducono drasticamente!! e quindi anche i tempi.
Qui di seguito la tabella "ridimensionata" a mio piacimento.. proprio a titolo di esempio, con tipi dato più piccoli:
CREATE TABLE OSM_Ord
(
[id] decimal(18, 0) IDENTITY (1, 1) NOT NULL,
SpA_Order varchar(30),
Sud_Order varchar(30),
NRPL_ShopOrder varchar (30),
TCC_Date smalldatetime NULL,
Prepack_IN_Date smalldatetime NULL,
Shipping_Date smalldatetime NULL,
Acceptance_Date smalldatetime NULL,
Allocation_Date smalldatetime NULL,
CONSTRAINT PK_ORD_Web PRIMARY KEY NONCLUSTERED
(
[id]
)
)
GO
CREATE CLUSTERED INDEX IX_OSM_Ord_Data ON OSM_Ord
(
TCC_Date
)
Ecco i risultati eseguendo le stesse query dei casi precedenti (filtro su tutta la tabella, filtro ristretto)
a) filtro esteso, solite 500000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 8988, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 438 ms, elapsed time = 10820 ms.
Letture e tempi ridotti drasticamente per la select con filtro esteso , piano Clustered Index Seek.
b) filtro ridotto, solite 300000 righe:
Table 'OSM_Ord'. Scan count 1, logical reads 5395, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 234 ms, elapsed time = 5411 ms.
Letture e tempi ridotti drasticamente per la select anche con filtro ridotto, piano Clustered Index Seek.
Ho allegato questo esempio, perchè è importante farti capire che il dimensionamento, gli indici, i tipi di indici, la modellazione delle tabelle sono tutte cose fondamentali da configurare per essere veloci. Meglio analizzi questa parte, più risultati positivi avrai. Cambiando non di molto alcuni campi, si ottiene addirittura un numero di letture di 3 volte più piccolo per i filtri estesi.. e più il filtro è mirato più le letture calano..
Ora, prendi pure la tua decisione , spero di essere stato sufficientemente chiaro!
ciao!
Alx81 =)
http://blogs.dotnethell.it/suxstellino