Eu tenho uma consulta como a seguinte:
DELETE FROM tblFEStatsBrowsers WHERE BrowserID NOT IN (
SELECT DISTINCT BrowserID FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID IS NOT NULL
)
tblFEStatsBrowsers possui 553 linhas.
tblFEStatsPaperHits possui 47.974.301 linhas.
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
)
Há um índice em cluster no tblFEStatsPaperHits que não inclui o BrowserID. A execução da consulta interna exigirá, portanto, uma verificação completa da tabela de tblFEStatsPaperHits - o que é totalmente aceitável.
Atualmente, uma varredura completa é executada para cada linha em tblFEStatsBrowsers, o que significa que eu tenho 553 varreduras de tabela completa de tblFEStatsPaperHits.
Reescrever apenas para WHERE EXISTS não altera o plano:
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
)
No entanto, conforme sugerido por Adam Machanic, adicionar uma opção HASH JOIN resulta no plano de execução ideal (apenas uma única varredura de tblFEStatsPaperHits):
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
) OPTION (HASH JOIN)
Agora, isso não é tanto uma questão de como corrigir isso - eu posso usar a OPTION (HASH JOIN) ou criar uma tabela temporária manualmente. Pergunto-me mais por que o otimizador de consultas jamais usaria o plano atualmente.
Como o QO não possui estatísticas na coluna BrowserID, suponho que esteja assumindo o pior - 50 milhões de valores distintos, exigindo, assim, uma grande mesa de trabalho na memória / tempdb. Dessa forma, a maneira mais segura é realizar verificações para cada linha no tblFEStatsBrowsers. Não há relação de chave estrangeira entre as colunas BrowserID nas duas tabelas, portanto, o QO não pode deduzir nenhuma informação de tblFEStatsBrowsers.
É este, por mais simples que pareça, o motivo?
Atualização 1
Para fornecer algumas estatísticas: OPTION (HASH JOIN):
208.711 leituras lógicas (12 varreduras)
OPÇÃO (LOOP JOIN, HASH GROUP):
11.008.698 leituras lógicas (~ varredura por BrowserID (339))
Nenhuma opção:
11.008.775 leituras lógicas (~ varredura por BrowserID (339))
Atualização 2
Excelentes respostas, todos vocês - obrigado! Difícil escolher apenas um. Embora Martin tenha sido o primeiro e Remus forneça uma excelente solução, eu tenho que entregá-lo ao Kiwi por se interessar nos detalhes :)