Це залежить від даних у ваших таблицях, ваших індексів, .... Важко сказати, не маючи можливості порівняти плани виконання / статистику io + часу.
Я б очікував на різницю - додаткове фільтрування, що відбувається перед ПРИЄДНАННЯМ між двома таблицями. У своєму прикладі я змінив оновлення на вибір, щоб повторно використовувати свої таблиці.
План виконання з "оптимізацією"
План виконання
Ви чітко бачите, що відбувається фільтрація, в моїх тестових даних немає записів, де відфільтровано, і як наслідок, ніяких поліпшень там не робиться.
План виконання, без "оптимізації"
План виконання
Фільтр відсутній, це означає, що нам доведеться розраховувати на приєднання, щоб відфільтрувати непотрібні записи.
Інша причина (а)
Ще однією причиною / наслідком зміни запиту може бути те, що при зміні запиту був створений новий план виконання, який відбувається швидше. Прикладом цього є двигун, який вибирає іншого оператора Join, але це лише здогадки.
Редагувати:
Уточнення після отримання двох планів запитів:
Запит читає 550М рядків з великої таблиці та фільтрує їх.
Це означає, що предикат - це той, хто робить більшу частину фільтрації, а не предикат. У результаті отримуються дані, що читаються, але набагато менше повертаються.
Змушення сервера sql використовувати інший індекс (план запитів) / додавання індексу може вирішити це.
Так чому запит на оптимізацію не має цього самого питання?
Оскільки використовується інший план запитів із скануванням замість пошуку.
Не виконуючи жодних пошуків, а лише повертаючи 4M рядки для роботи.
Наступна різниця
Не зважаючи на різницю оновлення (нічого не оновлюється в оптимізованому запиті) для оптимізованого запиту використовується хеш-відповідність:
Замість вкладеного циклу приєднуйтесь до неоптимізованого:
Вкладений цикл найкраще, коли одна таблиця мала, а інша - велика. Оскільки вони обидва близькі однакового розміру, я б стверджував, що хеш-матч - кращий вибір у цьому випадку.
Огляд
Оптимізований запит
План оптимізованого запиту має паралелізм, використовує з'єднання хеш-матів і йому потрібно робити менше залишкової фільтрації вводу-виводу. Він також використовує растрову карту для усунення ключових значень, які не можуть створювати жодних рядків з'єднання. (Також нічого не оновлюється)
Неоптимізований запит
План неоптимізованого запиту не має паралелізму, використовує вкладене з'єднання циклу і йому потрібно виконати залишкову фільтрацію вводу-виводу для записів 550M. (Також відбувається оновлення)
Що ви могли б зробити для покращення неоптимізованого запиту?
Зміна індексу на прізвище ім’я та прізвище у списку ключових стовпців:
СТВОРИТИ ІНДЕКС 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
слід робити те, що ви хочете там, не вимагаючи, щоб ви перераховували всі символи та не мали коду, який важко читати