Ho un tavolo come questo:
CREATE TABLE Updates
(
UpdateId INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
ObjectId INT NOT NULL
)
Sostanzialmente tenere traccia degli aggiornamenti degli oggetti con un ID crescente.
Il consumatore di questa tabella selezionerà un blocco di 100 ID oggetto distinti, ordinati per UpdateId
e partendo da uno specifico UpdateId
. In sostanza, tenere traccia di dove era stato interrotto e quindi eseguire una query per eventuali aggiornamenti.
Ho trovato che questo è un interessante problema di ottimizzazione perché sono stato in grado di generare un piano di query massimamente ottimale scrivendo query che capita di fare ciò che voglio a causa degli indici, ma non garantiscono ciò che voglio:
SELECT DISTINCT TOP 100 ObjectId
FROM Updates
WHERE UpdateId > @fromUpdateId
Dove si @fromUpdateId
trova un parametro della procedura memorizzata.
Con un piano di:
SELECT <- TOP <- Hash match (flow distinct, 100 rows touched) <- Index seek
A causa della ricerca UpdateId
sull'indice in uso, i risultati sono già corretti e ordinati come ID di aggiornamento dal più basso al più alto come voglio. E questo genera un piano distinto di flusso , che è quello che voglio. Ma l'ordinamento ovviamente non è un comportamento garantito, quindi non voglio usarlo.
Questo trucco comporta anche lo stesso piano di query (sebbene con un TOP ridondante):
WITH ids AS
(
SELECT ObjectId
FROM Updates
WHERE UpdateId > @fromUpdateId
ORDER BY UpdateId OFFSET 0 ROWS
)
SELECT DISTINCT TOP 100 ObjectId FROM ids
Tuttavia, non sono sicuro (e sospetto di no) se questo garantisce veramente l'ordinazione.
Una query che speravo che SQL Server fosse abbastanza intelligente da semplificare era questa, ma finisce per generare un piano di query molto scadente:
SELECT TOP 100 ObjectId
FROM Updates
WHERE UpdateId > @fromUpdateId
GROUP BY ObjectId
ORDER BY MIN(UpdateId)
Con un piano di:
SELECT <- Top N Sort <- Hash Match aggregate (50,000+ rows touched) <- Index Seek
Sto cercando di trovare un modo per generare un piano ottimale con una ricerca dell'indice attivata UpdateId
e un flusso distinto per rimuovere i duplicati ObjectId
. Qualche idea?
Dati di esempio se lo si desidera. Gli oggetti raramente avranno più di un aggiornamento e non dovrebbero quasi mai averne più di uno in un set di 100 righe, motivo per cui sto cercando un flusso distinto , a meno che non ci sia qualcosa di meglio che non conosco? Tuttavia, non esiste alcuna garanzia che un singolo ObjectId
non abbia più di 100 righe nella tabella. La tabella ha oltre 1.000.000 di righe e dovrebbe crescere rapidamente.
Supponiamo che l'utente abbia un altro modo per trovare il successivo appropriato @fromUpdateId
. Non è necessario restituirlo in questa query.