Ho una domanda di SQL avanzati per il suo SQL perf guru là fuori :-)SQL Server - non eseguire come previsto, non comportandosi come ho pensato che sarebbe
Attualmente sto cercando di capire un po 'di comportamento in un applicazione più grande, ma si riduce a una query su queste due tabelle:
Users
tavolo - circa 750 voci,UserId
(varchar(50)
) come PK clusterActionLog
tavola - di milioni di voci, includeUserId
- ma nessuna relazione FK
Per una griglia nella mia applicazione ASP.NET, sto cercando di ottenere tutti gli utenti oltre alla data dell'ultima voce di registro.
L'istruzione SQL che è attualmente in uso simile a questa:
SELECT
UserId, (other columns),
LastLogDate = (SELECT TOP (1) [Timestamp] FROM dbo.ActionLog a WHERE a.UserId = u.UserId ORDER BY [Timestamp] DESC)
FROM
dbo.Users u;
e restituisce le righe da mostrare - ma è piuttosto lento (circa 20 secondi.).
Il mio primo pensiero è stato quello di aggiungere un indice sul tavolo ActionLog
su UserId
e di includere la colonna Timestamp
in esso:
CREATE NONCLUSTERED INDEX [IDX_UserId]
ON [dbo].[ActionLog]([UserId] ASC)
INCLUDE ([Timestamp])
Le righe sono ora restituiti molto rapidamente - meno di 2 secondi, con 350'000 voci nella tabella ActionLog
e il mio indice viene utilizzato correttamente, come indicato dal piano di esecuzione. Tutto sembra a posto.
Ora, per approssimare lo scenario di produzione, abbiamo caricato circa 2 milioni di righe nella tabella ActionLog
, il 95% o più dei quali si riferiscono a un utente non-esistente (vale a dire queste righe hanno un UserId
che non esiste nel Tabella Users
).
Ora la query diventa estremamente lenta (24 minuti!) E l'indice non viene più utilizzato.
ho pensato che dal momento che la stragrande maggioranza delle voci nella tabella ActionLog
non si allineano con un utente esistente, vorrei vedere i guadagni di prestazioni se uso un indice filtrato - a "estirpare" tutti coloro disordinato le voci senza una corrispondente utente - così ho creato questo indice (che sostituisce l'altro che esisteva prima):
CREATE NONCLUSTERED INDEX [IDX_UserId]
ON [dbo].[Log]([UserId] ASC)
INCLUDE ([Timestamp])
WHERE UserId <> 'user' -- that's the fixed, non-existing "UserId" I wanted to avoid
Ma con mio grande sgomento - l'interrogazione è ancora circa lo stesso - dura più di 20 minuti per completare. Ho aggiornato le statistiche - nessuna modifica - ancora estremamente lento.
La cosa divertente (per me) è: quando ho rilasciato l'indice e lo ho ricreato -> ora la query è stata davvero molto veloce (ancora in meno di 3 secondi). WOW!
Ma non appena inizio ad aggiungere più voci, la query "si inclina" e diventa veramente molto lenta .......
Non capisco perché questo sta accadendo - stavo pensando che con un indice filtrato che elimina tutte quelle voci "canaglia", vedrei buone prestazioni nel cercare di trovare la più recente voce ActionLog
per gli utenti esistenti - ma questo non sembra essere il caso.
PERCHÉ NON?
Qualche idea? Pensieri? Cose da provare ??
di poter postare o descrivere il piano di esecuzione quando si rilascia e ricrea l'indice vs quando si inizia ad aggiungere più voci e si blocca? Sta solo decidendo di non usare l'indice dopo aver aggiunto le righe? – jimdrang
Prova ad aggiungere l'espressione filtro alla tua sottoquery correlata 'a.UserId <> 'utente''. Penso che l'ottimizzatore ne abbia bisogno per considerare l'indice filtrato. –
Esistono alcuni suggerimenti su come creare e utilizzare indici filtrati su MSDN. Si potrebbe provare a specificare 'WITH (INDEX (IDX_UserId))' alla fine della clausola 'SELECT'. Riferimento: [Crea indici filtrati] (http://msdn.microsoft.com/en-us/library/cc280372.aspx) –