To absolutnie nie jest odpowiedź kanoniczna, ale zauważyłem, że dla planów zapytania zagnieżdżonych pętli pokazanych w skrzynce SQL możliwe było zastosowanie planu z Zapytania 2 do Zapytania 1 za pomocą USE PLAN
podpowiedzi, ale próba odwrotnej operacji nie powiodła się z
Procesor zapytań nie mógł wygenerować planu zapytania, ponieważ podpowiedź USE PLAN zawiera plan, którego nie można zweryfikować pod kątem legalności zapytania. Usuń lub zastąp wskazówkę UŻYJ PLANU. Aby uzyskać największe prawdopodobieństwo pomyślnego wymuszenia planu, sprawdź, czy plan podany w podpowiedzi USE PLAN jest generowany automatycznie przez SQL Server dla tego samego zapytania.
Wyłączenie reguły transformacji optymalizatora ReorderLOJN
uniemożliwia także odniesienie do wcześniejszego pomyślnego planu.
Eksperymentowanie z większą ilością danych pokazuje, że SQL Server z pewnością jest w stanie przekształcić (A LOJ B) LOJ C
się również w A LOJ (B LOJ C)
naturalny sposób, ale nie widziałem żadnych dowodów na to, że sytuacja jest odwrotna.
Bardzo wymyślny przypadek, w którym pierwsze zapytanie działa lepiej niż drugie
DROP TABLE MyGrandChild , MyChild, MyParent
CREATE TABLE MyParent
(Id int)
CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)
CREATE TABLE MyGrandChild
(Id int
,ParentId int)
INSERT INTO MyChild
(Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values v1, master..spt_values
INSERT INTO MyGrandChild
(Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT gc.Id AS gcId,
gc.ParentId AS gcpId,
c.Id AS cId,
c.ParentId AS cpId,
p.Id AS pId
FROM MyGrandChild AS gc
LEFT OUTER JOIN MyChild AS c
ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN MyParent AS p
ON p.[Id] = c.[ParentId]
SELECT gc.Id AS gcId,
gc.ParentId AS gcpId,
c.Id AS cId,
c.ParentId AS cpId,
p.Id AS pId
FROM MyGrandChild AS gc
LEFT OUTER JOIN( MyChild AS c
LEFT OUTER JOIN MyParent AS p
ON p.[Id] = c.[ParentId])
ON c.[Id] = gc.[ParentId]
Co daje plany
Dla mnie zapytanie 1 miało czas 108 ms w porównaniu do 1163 ms dla zapytania 2.
Zapytanie 1
Table 'Worktable'. Scan count 0, logical reads 0
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5
Zapytanie 2
Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000
Table 'MyGrandChild'. Scan count 1, logical reads 7
Można więc wstępnie założyć, że pierwsza („nieoparta”) składnia jest potencjalnie korzystna, ponieważ pozwala na rozważenie większej liczby potencjalnych zleceń łączenia, ale nie przeprowadziłem wystarczająco wyczerpujących testów, aby mieć co do tego zaufanie.
Może być całkiem możliwe wymyślenie przykładów, w których Zapytanie 2 działa lepiej. Spróbuj obu i spójrz na plany wykonania.