Bu tablolar, indeksler, verilere bağlıdır .... Yürütme planları / io + zaman istatistiklerini karşılaştırmak mümkün olmadan söylemek zor.
Beklediğim fark, iki tablo arasında birleştirme önce ekstra filtreleme oluyor. Örneğimde, güncellemeleri tablolarımı yeniden kullanmak üzere seçimler olarak değiştirdim.
"Optimizasyon" ile yürütme planı
Yürütme planı
Bir filtre işleminin gerçekleştiğini açıkça görüyorsunuz, test verilerimde filtrelendiği yerlerde kayıt yok ve sonuç olarak hiçbir iyileştirme yapılmadı.
"Optimizasyon" olmadan yürütme planı
Yürütme planı
Filtre gitti, yani gereksiz kayıtları filtrelemek için birleşime güvenmemiz gerekecek.
Diğer nedenler
Sorguyu değiştirmenin başka bir nedeni / sonucu, sorguyu değiştirirken daha hızlı olan yeni bir yürütme planının oluşturulması olabilir. Bunun bir örneği, farklı bir Join operatörü seçen motordur, ancak bu sadece bu noktada tahmin etmektedir.
DÜZENLE:
İki sorgu planını aldıktan sonra açıklama:
Sorgu büyük tablodan 550M Satırları okuyor ve filtreliyor.
Yani yüklem, filtreleme işleminin çoğunu yapan, arama yüklemi değil. Sonuçta okunan verilerle sonuçlanır, ancak daha az geri gönderilir.
Sql sunucusunun farklı bir dizin (sorgu planı) kullanması / bir dizin eklenmesi bu sorunu çözebilir.
Peki, en iyileştirme sorgusu neden aynı soruna sahip değil?
Çünkü arama yerine tarama ile farklı bir sorgu planı kullanılır.
Herhangi bir arama yapmadan, sadece çalışmak için 4M satırları döndürmek.
Sonraki fark
Güncelleme farkını göz ardı ederek (optimize edilmiş sorguda hiçbir şey güncellenmiyor) optimize edilmiş sorguda bir karma eşleme kullanılır:
Optimize edilmemiş bir iç içe döngü birleşimi yerine:
İç içe döngü, bir tablo küçük ve diğeri büyük olduğunda en iyisidir. Her ikisi de aynı boyuta yakın olduğu için, karma eşlemenin bu durumda daha iyi bir seçim olduğunu iddia ediyorum.
genel bakış
Optimize edilmiş sorgu
Optimize edilmiş sorgunun planı paralellizme sahiptir, karma eşleşme birleşimini kullanır ve daha az kalıntı GÇ filtrelemesi yapması gerekir. Ayrıca, herhangi bir birleştirme satırı oluşturamayan anahtar değerleri ortadan kaldırmak için bir bitmap kullanır. (Ayrıca hiçbir şey güncellenmiyor)
Optimize edilmemiş sorgu Optimize edilmemiş sorgunun planında paralellik yoktur, iç içe döngü birleşimi kullanır ve 550M kayıtlarında artık IO filtrelemesi yapılması gerekir. (Ayrıca güncelleme oluyor)
Optimize edilmemiş sorguyu geliştirmek için ne yapabilirsiniz?
Dizini, anahtar sütun listesinde ad ve soyadı olacak şekilde değiştirme:
Dbo.largeTableOfPeople (doğum_tarihi, ad_adı, soyadı) üzerinde IX_largeTableOfPeople_birth_date_first_name_last_name CREATE
Ancak fonksiyonların kullanımı ve bu tablonun büyük olması nedeniyle bu en uygun çözüm olmayabilir.
- İstatistikleri güncellemek, daha iyi bir plan elde etmek için yeniden derleme kullanarak.
(HASH JOIN, MERGE JOIN)
Sorguya OPTION ekleme
- ...
Test verileri + Kullanılan sorgular
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
tüm karakterleri listelemenize ve okunması zor bir koda sahip olmanıza gerek kalmadan orada istediğinizi yapmalısınız