Ho una domanda come la seguente:
DELETE FROM tblFEStatsBrowsers WHERE BrowserID NOT IN (
SELECT DISTINCT BrowserID FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID IS NOT NULL
)
tblFEStatsBrowsers ha 553 righe.
tblFEStatsPaperHits ha 47.974.301 righe.
tblFEStatsBrowsers:
CREATE TABLE [dbo].[tblFEStatsBrowsers](
[BrowserID] [smallint] IDENTITY(1,1) NOT NULL,
[Browser] [varchar](50) NOT NULL,
[Name] [varchar](40) NOT NULL,
[Version] [varchar](10) NOT NULL,
CONSTRAINT [PK_tblFEStatsBrowsers] PRIMARY KEY CLUSTERED ([BrowserID] ASC)
)
tblFEStatsPaperHits:
CREATE TABLE [dbo].[tblFEStatsPaperHits](
[PaperID] [int] NOT NULL,
[Created] [smalldatetime] NOT NULL,
[IP] [binary](4) NULL,
[PlatformID] [tinyint] NULL,
[BrowserID] [smallint] NULL,
[ReferrerID] [int] NULL,
[UserLanguage] [char](2) NULL
)
C'è un indice cluster su tblFEStatsPaperHits che non include BrowserID. L'esecuzione della query interna richiederà quindi una scansione completa della tabella di tblFEStatsPaperHits, il che è totalmente OK.
Attualmente, viene eseguita una scansione completa per ogni riga in tblFEStatsBrowsers, il che significa che ho 553 scansioni complete di tblFEStatsPaperHits.
La riscrittura in solo DOVE ESISTE non cambia il piano:
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
)
Tuttavia, come suggerito da Adam Machanic, l'aggiunta di un'opzione HASH JOIN porta al piano di esecuzione ottimale (solo una singola scansione di tblFEStatsPaperHits):
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
) OPTION (HASH JOIN)
Ora questa non è una domanda su come risolvere questo problema: posso usare OPTION (HASH JOIN) o creare manualmente una tabella temporanea. Mi chiedo di più perché il Query Optimizer avrebbe mai usato il piano che attualmente fa.
Dal momento che il QO non ha statistiche sulla colonna BrowserID, suppongo che stia assumendo il peggio - 50 milioni di valori distinti, quindi richiede un tavolo di lavoro in memoria / tempdb abbastanza grande. Pertanto, il modo più sicuro è eseguire scansioni per ogni riga in tblFEStatsBrowsers. Non esiste alcuna relazione di chiave esterna tra le colonne BrowserID nelle due tabelle, quindi il QO non può detrarre alcuna informazione da tblFEStatsBrowsers.
È questo, semplice come sembra, il motivo?
Aggiornamento 1
Per fornire un paio di statistiche: OPZIONE (HASH JOIN):
208.711 letture logiche (12 scansioni)
OPZIONE (LOOP JOIN, HASH GROUP):
11.008.698 letture logiche (~ scan per BrowserID (339))
Nessuna opzione:
11.008.775 letture logiche (~ scan per BrowserID (339))
Aggiornamento 2
Risposte eccellenti, tutti voi - grazie! Difficile sceglierne solo uno. Anche se Martin è stato il primo e Remus offre una soluzione eccellente, devo darlo al Kiwi per andare mentalmente sui dettagli :)