Depende de los datos en sus tablas, sus índices, ... Difícil de decir sin poder comparar los planes de ejecución / las estadísticas io + time.
La diferencia que esperaría es el filtrado adicional que ocurre antes de la UNIÓN entre las dos tablas. En mi ejemplo, cambié las actualizaciones a selecciones para reutilizar mis tablas.
El plan de ejecución con "la optimización"
Plan de ejecución
Usted ve claramente que ocurre una operación de filtro, en mis datos de prueba no se registraron registros y, como resultado, no se realizaron mejoras.
El plan de ejecución, sin "la optimización"
Plan de ejecución
El filtro se ha ido, lo que significa que tendremos que confiar en la unión para filtrar los registros innecesarios.
Otra (s) razón (es)
Otra razón / consecuencia de cambiar la consulta podría ser, que se creó un nuevo plan de ejecución al cambiar la consulta, que resulta ser más rápido. Un ejemplo de esto es el motor que elige un operador de Unión diferente, pero eso es solo adivinar en este punto.
EDITAR:
Aclarando después de obtener los dos planes de consulta:
La consulta lee 550 millones de filas de la tabla grande y las filtra.
Lo que significa que el predicado es el que realiza la mayor parte del filtrado, no el predicado de búsqueda. Como resultado, los datos se leen, pero mucho menos se devuelven.
Hacer que el servidor sql use un índice diferente (plan de consulta) / agregar un índice podría resolver esto.
Entonces, ¿por qué la consulta de optimización no tiene el mismo problema?
Porque se utiliza un plan de consulta diferente, con un escaneo en lugar de una búsqueda.
Sin hacer ninguna búsqueda, pero solo devolviendo 4M filas para trabajar.
Siguiente diferencia
Sin tener en cuenta la diferencia de actualización (no se actualiza nada en la consulta optimizada), se utiliza una coincidencia hash en la consulta optimizada:
En lugar de una unión de bucle anidado en el no optimizado:
Un bucle anidado es mejor cuando una tabla es pequeña y la otra grande. Como ambos están cerca del mismo tamaño, diría que la coincidencia de hash es la mejor opción en este caso.
Visión de conjunto
La consulta optimizada
El plan de la consulta optimizada tiene paralelismo, utiliza una combinación de coincidencia hash y necesita hacer menos filtrado de E / S residual. También usa un mapa de bits para eliminar los valores clave que no pueden producir filas de unión. (Tampoco se actualiza nada)
La consulta
no optimizada El plan de la consulta no optimizada no tiene paralelismo, utiliza una unión de bucle anidado y necesita hacer un filtrado de E / S residual en los registros de 550M. (También está ocurriendo la actualización)
¿Qué podría hacer para mejorar la consulta no optimizada?
Cambiar el índice para tener nombre_nombre & apellido_en la lista de columnas clave:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name en dbo.largeTableOfPeople (birth_date, first_name, last_name) include (id)
Pero debido al uso de funciones y a que esta tabla es grande, esta podría no ser la solución óptima.
- Actualización de estadísticas, usando recompilar para tratar de obtener el mejor plan.
- Agregar OPCIÓN
(HASH JOIN, MERGE JOIN)
a la consulta
- ...
Datos de prueba + consultas utilizadas
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
debe hacer lo que quiera allí sin requerir que enumere todos los caracteres y tenga un código que sea difícil de leer