Це абсолютно не канонічна відповідь, але я помітив, що для вкладених планів запитів циклів, показаних у скрипті SQL, можна застосувати план із запиту 2 до запиту 1 з використанням USE PLAN
підказки, але спроба зворотної операції не вдається
Процесор запиту не зміг скласти план запитів, оскільки підказка USE PLAN містить план, який не можна було перевірити як законний для запиту. Видаліть або замініть ПІДКЛЮЧЕННЯ ПЛАНУ. Для найкращої ймовірності успішного форсування плану переконайтеся, що план, наданий у підказці USE PLAN, є генерованим автоматично SQL сервером для того ж запиту.
Вимкнення правила перетворення оптимізатора ReorderLOJN
запобігає успіху попереднього успішного натяку на план.
Експерименти з більшою кількістю даних показують, що SQL Server, безумовно, здатний перетворитись (A LOJ B) LOJ C
і A LOJ (B LOJ C)
природним шляхом, але я не побачив жодних доказів того, що зворотне дійсне.
Дуже надуманий випадок, коли перший запит працює краще, ніж другий
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]
Що дає плани
Для мене для запиту 1 минув час 108 мс проти 1163 мс для запиту 2.
Запит 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
Запит 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
Таким чином, може бути попередньо припущено, що перший ("непропущений") синтаксис є потенційно вигідним, оскільки дозволяє розглянути більше потенційних замовлень на приєднання, але я не зробив достатньо вичерпних тестів, щоб мати велику впевненість у цьому як загальне правило.
Можливо, цілком можливо придумати зустрічні приклади, коли запит 2 працює краще. Спробуйте обидва і подивіться плани виконання.