Это зависит от данных в ваших таблицах, ваших индексов ... Трудно сказать, не имея возможности сравнить планы выполнения / статистику времени io +.
Разница, которую я ожидаю, заключается в дополнительной фильтрации, происходящей перед соединением между двумя таблицами. В моем примере я изменил обновления для выбора для повторного использования моих таблиц.
План выполнения с «оптимизацией»
План выполнения
Вы четко видите, что происходит операция фильтрации, в моих тестовых данных нет записей, где отфильтровываются, и, как следствие, нет улучшений, где сделаны
План выполнения, без «оптимизации»
План выполнения
Фильтр пропал, а это значит, что нам придется полагаться на объединение, чтобы отфильтровать ненужные записи.
Другая (-ые) причина (-ы)
Другая причина / следствие изменения запроса может заключаться в том, что при изменении запроса был создан новый план выполнения, который оказывается быстрее. Примером этого является движок, выбирающий другой оператор Join, но на данный момент это только предположение.
РЕДАКТИРОВАТЬ:
Уточнение после получения двух планов запроса:
Запрос читает 550M строк из большой таблицы и отфильтровывает их.
Это означает, что именно предикат выполняет большую часть фильтрации, а не предикат поиска. В результате данные читаются, но возвращаются меньше.
Заставить SQL Server использовать другой индекс (план запроса) / добавление индекса может решить эту проблему.
Так почему же запрос на оптимизацию не имеет такой же проблемы?
Потому что используется другой план запроса, с поиском вместо поиска.
Без каких-либо поисков, но только возвращая 4M строк для работы.
Следующая разница
Не обращая внимания на разницу в обновлении (в оптимизированном запросе ничего не обновляется), в оптимизированном запросе используется совпадение хеша:
Вместо неоптимизированного соединения с вложенным циклом:
Вложенный цикл лучше всего подходит, когда одна таблица маленькая, а другая большая. Поскольку они оба близки по размеру, я бы сказал, что в этом случае лучшим выбором будет совпадение по хешу.
обзор
Оптимизированный запрос
План оптимизированного запроса имеет параллелизм, использует соединение с хэш-соответствием и требует меньше остаточной фильтрации ввода-вывода. Он также использует растровое изображение для исключения значений ключей, которые не могут создавать строки соединения. (Тоже ничего не обновляется)
Неоптимизированный запрос План неоптимизированного запроса не имеет параллелизма, использует соединение с вложенным циклом и должен выполнять остаточную фильтрацию ввода-вывода для записей 550M. (Также происходит обновление)
Что вы можете сделать, чтобы улучшить неоптимизированный запрос?
Изменение индекса, чтобы в списке ключевых столбцов были указаны имя и фамилия:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name для dbo.largeTableOfPeople (дата рождения, имя_первого, фамилия) включают (id)
Но из-за использования функций и большой таблицы это может быть не оптимальным решением.
- Обновление статистики с использованием перекомпиляции, чтобы попытаться получить лучший план.
- Добавление OPTION
(HASH JOIN, MERGE JOIN)
к запросу
- ...
Тестовые данные + использованные запросы
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
делать то, что вы хотите, не требуя перечисления всех символов и иметь код, который трудно прочитать