Я намагаюся скласти зразок плану запитів, щоб показати, чому UNIONing двох наборів результатів може бути кращим, ніж використання АБО в пункті JOIN. План запитів, який я написав, мене наткнув. Я використовую базу даних StackOverflow з некластеризованим індексом на Users.Reputation.
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
План запитів знаходиться за адресою https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE , тривалість запиту для мене становить 4:37 хв., 26612 рядків повернуто.
Раніше я не бачив, щоб цей стиль постійного сканування створювався з існуючої таблиці - я не знайомий з тим, чому існує постійне сканування для кожного окремого ряду, коли постійне сканування зазвичай використовується для одного рядка, введеного користувачем наприклад SELECT GETDATE (). Чому він використовується тут? Я дуже вдячний, щоб прочитати цей план запитів.
Якщо я розділяю це АБО на UNION, він створює стандартний план, який працює за 12 секунд з тими ж поверненими 26612 рядками.
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Я трактую цей план так:
- Отримайте всі 41782500 рядків із публікацій (фактична кількість рядків відповідає скануванню CI у повідомленнях)
- Кожні 41782500 рядків у повідомленнях:
- Виробляють скаляри:
- Expr1005: OwnerUserId
- Expr1006: OwnerUserId
- Expr1004: Статичне значення 62
- Expr1008: LastEditorUserId
- Expr1009: LastEditorUserId
- Expr1007: Статичне значення 62
- У конкатенаті:
- Exp1010: Якщо Expr1005 (OwnerUserId) не є нульовим, використовуйте інше використання Expr1008 (LastEditorUserID)
- Expr1011: Якщо Expr1006 (OwnerUserId) не є нульовим, використовуйте це, інакше використовуйте Expr1009 (LastEditorUserId)
- Expr1012: Якщо значення Expr1004 (62) недійсне, використовуйте Expr1007 (62)
- У скалярі обчислень: Я не знаю, що робить амперсанд.
- Expr1013: 4 [і?] 62 (Expr1012) = 4, а OwnerUserId IS NULL (NULL = Expr1010)
- Expr1014: 4 [і?] 62 (Expr1012)
- Expr1015: 16 та 62 (Expr1012)
- У порядку замовлення за сортуванням:
- Expr1013 Опис
- Expr1014 Asc
- Expr1010 Вих
- Expr1015 Опис
- У інтервалі злиття було видалено Expr1013 та Expr1015 (це входи, але не виходи)
- В індексі пошуку нижче вкладених циклів приєднуйтесь, він використовує Expr1010 та Expr1011 як предикати для пошуку, але я не розумію, яким чином він має доступ до них, коли він не зробив вкладений цикл з IX_NC_REPUTATION до піддерева, що містить Expr1010 та Expr1011 .
- Приєднання вкладених циклів повертає лише Users.ID, які мають відповідність у попередньому піддереві. Через натискання присудка повертаються всі рядки, повернені з індексу, шукані в IX_NC_REPUTATION.
- Останній приєднаний цикл приєднання: для кожного запису повідомлень виведіть Users.Id, де відповідність знайдена в наборі даних нижче.
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;