Eu tenho uma consulta que envolve uma pesquisa de texto completo como esta:
SELECT TOP 30 PersonId,
PersonParentId,
PersonName,
PersonPostCode
FROM dbo.People
WHERE PersonDeletionDate IS NULL
AND PersonCustomerId = 24
AND CONTAINS(ContactFullText, '"mr" AND "ch*"')
AND PersonGroupId IN(197, 206, 186, 198)
ORDER BY PersonParentId,
PersonName;
Isso gera dois planos principais, um é muito rápido em todos os casos, o outro é muito lento na maioria dos casos.
Eu experimentei essa consulta de modo que a pesquisa do FT não seja incluída e o que descobri é que as estimativas de linha são sempre muito menores do que deveriam.
Se eu executar update statistics...with fullscan
, ainda vejo estimativas de linha extremamente imprecisas das operações de busca do índice NC no plano de execução.
Quando as estimativas de linha são baixas o suficiente, uma junção de loop é selecionada, o que normalmente é muito lento (mais de 30 segundos). Estimativas mais altas parecem produzir um bom plano envolvendo uma junção de mesclagem em vez de uma junção de loop.
Por que o SQL Server ainda não estima as contagens de linhas, apesar de ainda ter estatísticas atualizadas?
O plano: https://www.brentozar.com/pastetheplan/?id=rkXtE0jzX
Quando removo a CONTAINS
parte, omitindo assim a pesquisa FullText, a consulta é rápida, mas a estimativa de linha para a busca de índice ainda é 1 estimada, 2195 reais.
Seguindo o conselho de @Kin, usei CONTAINSTABLE, que funcionou instantaneamente e produziu o seguinte plano: https://www.brentozar.com/pastetheplan/?id=S1hKainzQ Interessante que não existe um operador de pesquisa Full Text.
O Containstable requer RANK
para produzir o mesmo conjunto de resultados neste caso que usei AND RANK > 0
no WHERE
para produzir os resultados que quero, que produz este plano: https://www.brentozar.com/pastetheplan/?id=B1U7AA2zm
Minha única pergunta agora é sobre por que as estimativas de linha ainda são imprecisas, mas me importo menos agora que minhas consultas FT parecem significativamente mais rápidas e confiáveis. Muito satisfeito! https://www.brentozar.com/pastetheplan/?id=B1U7AA2zm
@EvanCarroll histograma de estatísticas aqui: https://pastebin.com/p7s0NvX5
Algumas informações de acompanhamento - planos de execução antes/depois de algumas consultas de pesquisa típicas do FT para o aplicativo que está sendo suportado
UMA
- Antes: https://www.brentozar.com/pastetheplan/?id=SJlAAAN7X (5 segundos)
- Depois: https://www.brentozar.com/pastetheplan/?id=H1ltkkSmm (<1 segundo)
B
- Antes: https://www.brentozar.com/pastetheplan/?id=Sy-gxJBQm (40 segundos)
- Depois: https://www.brentozar.com/pastetheplan/?id=Sy2VxJrm7 (1 segundo)
C
- Antes: https://www.brentozar.com/pastetheplan/?id=r1z5e1rQ7 (2 segundos)
- Depois: https://www.brentozar.com/pastetheplan/?id=r1oplkSQm (<1 segundo)
D
- Antes: https://www.brentozar.com/pastetheplan/?id=B1kHf1BQQ (2 minutos e 20 segundos)
- Depois: https://www.brentozar.com/pastetheplan/?id=r1D5z1SQm (11 segundos)
(resumindo meus comentários e colocando como resposta)
Uma reescrita de consulta resolverá o problema de obter estimativas de linhas baixas. Como Joe Chang explica em sua postagem no blog Query Optimizer Gone Wild - Full-Text
Você obtém um plano muito melhor (junção de mesclagem) usando
CONTAINSTABLE
versus o plano real usandocontains
uma junção de loop aninhada com estimativas de linhas baixas .Você pode reescrever a consulta como:
As consultas de texto completo são parcialmente recompiladas com base no texto na cláusula contém. (Por experiência) vou arriscar um palpite de que o SQL Server está esperando um número baixo de linhas dos predicados relacionais e está fazendo um loop for-each "seek" no mecanismo FTS. As buscas podem ser assassinos de desempenho.
Se você deseja um desempenho previsível, pode dividir a consulta em duas partes.
Você não deveria ter que fazer isso, mas funciona.