Depende dos dados em suas tabelas, índices, ... Difícil dizer sem poder comparar os planos de execução / as estatísticas de tempo io +.
A diferença que eu esperaria é a filtragem extra acontecendo antes do JOIN entre as duas tabelas. No meu exemplo, alterei as atualizações para seleções para reutilizar minhas tabelas.
O plano de execução com "a otimização"
Plano de execução
Você vê claramente uma operação de filtro acontecendo. Nos meus dados de teste, nenhum registro foi filtrado e, como resultado, nenhuma melhoria foi feita.
O plano de execução, sem "a otimização"
Plano de execução
O filtro acabou, o que significa que teremos que confiar na junção para filtrar registros desnecessários.
Outro motivo (s)
Outro motivo / consequência da alteração da consulta pode ser o fato de um novo plano de execução ter sido criado ao alterar a consulta, o que é mais rápido. Um exemplo disso é o mecanismo que escolhe um operador Join diferente, mas isso é apenas uma suposição neste momento.
EDITAR:
Esclarecendo depois de obter os dois planos de consulta:
A consulta está lendo Linhas de 550M da tabela grande e filtrando-as.
Significando que o predicado é quem realiza a maior parte da filtragem, não o predicado de busca. Resultando na leitura dos dados, mas muito menos sendo retornados.
Fazer o servidor sql usar um índice diferente (plano de consulta) / adicionar um índice pode resolver isso.
Então, por que a consulta de otimização não tem esse mesmo problema?
Como um plano de consulta diferente é usado, com uma varredura em vez de uma busca.
Sem fazer nenhuma busca, mas retornando apenas 4 milhões de linhas para trabalhar.
Próxima diferença
Desconsiderando a diferença de atualização (nada está sendo atualizado na consulta otimizada), uma correspondência de hash é usada na consulta otimizada:
Em vez de uma junção de loop aninhado no não otimizado:
Um loop aninhado é melhor quando uma tabela é pequena e a outra grande. Como os dois são do mesmo tamanho, eu argumentaria que a combinação de hash é a melhor escolha nesse caso.
visão global
A consulta otimizada
O plano da consulta otimizada tem paralelismo, usa uma junção de combinação de hash e precisa fazer menos filtragem de E / S residual. Ele também usa um bitmap para eliminar valores-chave que não podem produzir nenhuma linha de junção. (Também nada está sendo atualizado)
A consulta
não otimizada O plano da consulta não otimizada não tem paralelismo, usa uma junção de loop aninhada e precisa fazer a filtragem de E / S residual em registros de 550 milhões. (Também a atualização está acontecendo)
O que você poderia fazer para melhorar a consulta não otimizada?
Alterando o índice para ter first_name e last_name na lista de colunas-chave:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name em dbo.largeTableOfPeople (data de nascimento, nome e sobrenome) include (id)
Porém, devido ao uso de funções e a tabela ser grande, essa pode não ser a solução ideal.
- Atualizando estatísticas, usando recompilar para tentar obter o melhor plano.
- Adicionando OPTION
(HASH JOIN, MERGE JOIN)
à consulta
- ...
Dados de teste + consultas usadas
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
faça o que quiser lá, sem exigir que você liste todos os caracteres e tenha um código difícil de ler