Tenho a seguinte consulta:
ALTER PROCEDURE [dbo].[spSearchClient]
@SearchWords NVARCHAR(MAX) = NULL,
@LowerDate DATE = NULL,
@UpperDate DATE = NULL,
@UserCreated nvarchar(450)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @UserAccountID smallint
DECLARE @SearchWordCount int
SELECT @UserAccountID = dbo.fnGetUserAccountID(@UserCreated)
CREATE TABLE #SearchWords
(
ID int IDENTITY(1,1),
Word NVARCHAR(50)
)
INSERT INTO #SearchWords
(
Word
)
SELECT
value
FROM
STRING_SPLIT(@SearchWords, ' ')
WHERE
TRIM(value) <> ''
SELECT @SearchWordCount = @@ROWCOUNT;
SELECT
C.ClientID,
C.FirstName,
C.LastName,
C.FullName,
C.DateOfBirth,
G.GenderName,
G.GenderIcon,
C.VerificationCode,
V.LastVisitDate
FROM
Client C
OUTER APPLY (
SELECT MAX(StartDate) AS LastVisitDate
FROM Visit AS V
WHERE C.ClientID = V.ClientID
) AS V
INNER JOIN LookUp.Gender G on
C.GenderID = G.GenderID
WHERE
(
EXISTS( -- if we have words
SELECT *
FROM #SearchWords s
WHERE (c.FirstName LIKE CONCAT('%',s.Word,'%'))
OR (c.LastName LIKE CONCAT('%',s.Word,'%'))
OR (c.VerificationCode LIKE CONCAT('%',s.Word,'%'))
)
OR @SearchWordCount = 0 --if we don't have words
)
AND DateOfBirth BETWEEN ISNULL(@LowerDate,DateOfBirth) AND ISNULL(@UpperDate,DateOfBirth)
INSERT INTO UserSearchLog
(
SearchWords,
LowerDate,
UpperDate,
SearchResultsCount,
UserCreated
)
VALUES
(
@SearchWords,
@LowerDate,
@UpperDate,
@@ROWCOUNT,
@UserAccountID
)
DROP TABLE #SearchWords
END
O plano de execução é https://www.brentozar.com/pastetheplan/?id=BkGzfUeHz
A consulta funciona como deveria, mas leva de 3 a 7 segundos para ser executada e parece ser devido ao seguinte, que tem uma execução para cada linha na tabela Client:
WHERE
(
EXISTS( -- if we have words
SELECT *
FROM #SearchWords s
WHERE (c.FirstName LIKE CONCAT('%',s.Word,'%'))
OR (c.LastName LIKE CONCAT('%',s.Word,'%'))
OR (c.VerificationCode LIKE CONCAT('%',s.Word,'%'))
)
OR @SearchWordCount = 0 --if we don't have words
)
Quer saber se alguém conhece uma maneira melhor e mais eficaz de fazer isso que consumiria menos tempo?
Se alguns dados de exemplo forem úteis, por favor me avise.
Parece haver um problema com o servidor que impedirá que você obtenha um bom desempenho com essa consulta, mesmo quando
@SearchWordCount = 0
. Considere os detalhes do ID do nó 15:Este plano é executado em modo de linha e este operador está no final da ramificação, então você pode atribuir todos os 20 ms de tempo de CPU e 3962 ms de tempo decorrido à busca de índice em
[Visit].[IDX_Visit_ClientID]
. Ao executar esta consulta, você gastou quase quatro segundos esperando por algo diferente do trabalho da CPU. Observar as estatísticas de espera para o operador de seleção fornece uma pista valiosa:Quase quatro segundos esperando em PAGEIOLATCH_SH . Podemos obter mais informações sobre o IO que foi realmente feito observando o RunTimeInformation no XML:
O SQL Server precisou fazer apenas 50 leituras físicas para a busca de índice, mas esperou quase quatro segundos para fazê-las. Você está lendo dados a uma taxa de 100 KB por segundo. Pode haver um problema com a configuração do servidor ou talvez o servidor esteja sobrecarregado. Paul Randal publicou instruções detalhadas aqui para determinar a causa raiz do problema e como corrigi-lo. Boa sorte.
Sobre a correspondência de padrões, o SQL Server levou cerca de um terço de segundo para fazer isso com apenas uma única linha na tabela temporária. Essa parte da consulta pode se tornar um gargalo à medida que o número de linhas aumenta na tabela temporária. Mas você não obterá um bom desempenho alterando essa parte da consulta sem resolver o problema com
PAGEIOLATCH_SH
esperas primeiro.Bem menor
A considerar
Remover palavras que contenham uma palavra menor
Não há nenhum propósito em pesquisar por 'recuo' se você pesquisou por 'em'
Abordarei um trabalho de tentativa e erro muito simples, começando com:
As principais pesquisas de curingas são péssimas. Se você realmente precisa disso, então, até certo ponto, precisará aceitar esse simples fato e ver o que mais pode fazer, inclusive alterar o requisito de negócios.
No mínimo, veja se você pode alterar o requisito para exigir que a primeira letra esteja correta!
c.FirstName LIKE '%[TDH][oia]%' ESCAPE '|'