A consulta a seguir leva cerca de 10 segundos para terminar em uma tabela com 12k registros
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Mas se eu mudar a cláusula where para
where "id" = 1
ou
where contains("lastName", '"a*"')
Ele retornará instantaneamente.
Ambas as colunas são indexadas e a coluna lastName também é indexada em texto completo.
CREATE TABLE Physician
(
id int identity NOT NULL,
firstName nvarchar(100) NOT NULL,
lastName nvarchar(100) NOT NULL
);
ALTER TABLE Physician
ADD CONSTRAINT Physician_PK
PRIMARY KEY CLUSTERED (id);
CREATE NONCLUSTERED INDEX Physician_IX2
ON Physician (firstName ASC);
CREATE NONCLUSTERED INDEX Physician_IX3
ON Physician (lastName ASC);
CREATE FULLTEXT INDEX
ON "Physician" ("firstName" LANGUAGE 0x0, "lastName" LANGUAGE 0x0)
KEY INDEX "Physician_PK"
ON "the_catalog"
WITH stoplist = off;
Aqui está o plano de execução
Qual poderia ser o problema?
sql-server
sql-server-2008-r2
full-text-search
Hooman Valibeigi
fonte
fonte
Respostas:
Seu plano de execução
Ao analisar o plano de consulta, podemos ver que um índice é tocado para atender a duas operações de filtro.
Em termos simples, devido ao operador TOP, uma meta de linha foi definida. Muito mais informações e pré-requisitos sobre metas de linha podem ser encontrados aqui
Da mesma fonte:
A tabela inteira é sondada nos filtros com o uso de uma semi junção esquerda que tem uma meta de linha definida, na esperança de retornar as 5 linhas o mais rápido e eficiente possível.
Isso não acontece, resultando em muitas iterações sobre o TVF .Fulltextmatch.
Recriando
Com base no seu plano , consegui recriar um pouco o seu problema:
Executando a consulta
Resultados em um plano de consulta comparável ao seu:
No exemplo acima, B não existe no índice de texto completo. Como resultado, depende do parâmetro e dos dados a eficiência do plano de consulta.
Uma explicação melhor disso pode ser encontrada em Row Goals, Part 2: Semi Joins por Paul White
Por exemplo, alterando o predicado para que os resultados sejam encontrados mais cedo (no início da varredura).
o
where "id" = 124
é eliminado devido ao predicado do índice de texto completo já retornar 5 linhas, satisfazendo oTOP()
predicado.Os resultados mostram isso também
E as execuções de TVF:
Inserindo algumas novas linhas
Executando a consulta para encontrar essas linhas inseridas anteriores
Isso novamente resulta em muitas iterações em quase todas as linhas para retornar o último, mas um valor encontrado.
Resolver
Ao remover o objetivo da linha usando o traceflag 4138
O otimizador usa um padrão de junção mais próximo da implementação de a
UNION
; no nosso caso, isso é favorável, pois empurra os predicados para as respectivas buscas de índice em cluster e não usa o operador de semi junção esquerda com objetivo de linha.Outra maneira de escrever isso, sem usar o traceflag acima mencionado:
Com o plano de consulta resultante:
onde a função de texto completo é aplicada diretamente
Como nota de rodapé, por op, o hotfix do otimizador de consultas traceflag 4199 resolveu seu problema. Ele implementou isso adicionando
OPTION(QUERYTRACEON(4199))
à consulta. Não fui capaz de reproduzir esse comportamento do meu lado. Esse hotfix contém uma otimização de semi-junção:Fonte
Extra
Durante a otimização baseada em custos, o otimizador também pode adicionar um spool de índice ao plano de execução, implementado por
LogOp_Spool Index on fly Eager
(ou a contraparte física)Faz isso com meu conjunto de dados para,
TOP(3)
mas não paraTOP(2)
Fonte
Com o predicado de busca aplicado a este spool ansioso do índice:
fonte