SQL приєднується до протизапитів SQL (продуктивність)?


110

Я хочу знати, чи є у мене запит на приєднання щось подібне -

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

і підпиту щось подібне -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

Коли я розглядаю ефективність, який із двох запитів був би швидшим і чому ?

Чи є час, коли я повинен віддавати перевагу одному перед іншим?

Вибачте, якщо це занадто банально і просили раніше, але я з цим розгублений. Крім того, було б чудово, якщо ви, хлопці, можете запропонувати мені інструменти, які я повинен використовувати для вимірювання ефективності двох запитів. Дуже дякую!


5
@Lucero, це питання позначено тегом sql-server-2008, де публікація, яку ви згадуєте, позначена MySql. Ви можете зробити висновок, що відповіді будуть однаковими. Оптимізація продуктивності проводиться по-різному на двох RDBMS.
Франсуа Бота

Відповіді:


48

Я б очікував, що перший запит буде швидшим, головним чином тому, що у вас є еквівалентність і явна ПРИЄДНАЙТЕСЬ. На мій досвід IN, це дуже повільний оператор, оскільки SQL зазвичай оцінює його як ряд WHEREпропозицій, розділених "АБО" ( WHERE x=Y OR x=Z OR...).

Як і у ВСІХ ЧАСАХ SQL, проте пробіг може відрізнятися. Швидкість буде багато залежати від індексів (чи є у вас індекси в обох стовпцях ідентифікаторів? Це дуже допоможе ...) серед іншого.

Єдиний РЕАЛЬНИЙ спосіб сказати зі 100% впевненістю, що швидше, - увімкнути відстеження ефективності (особливо корисна інформація IO) та запустити їх обох. Не забудьте очистити кеш-пам'ять між прогонами!


16
У мене є серйозні сумніви щодо цієї відповіді, оскільки більшість СУБД, безумовно, SQL Server 2008 та пізніші версії, переводять підпит на єдиний ідентифікатор (не співвіднесений, що означає: не посилання на декілька зовнішніх стовпців запитів) у відносно швидке напівз'єднання. Крім того, як раніше зазначалося в іншій відповіді, перше, справжнє приєднання поверне рядок для ВСІХ виникнення відповідного ідентифікатора в Dept - це не має значення для унікального ідентифікатора, але дасть вам багато дублікатів в іншому місці. Порівнювати їх за допомогою DISTINCT або GROUP BY буде ще одним важким навантаженням. Перевірте плани виконання в студії управління SQL Server!
Ерік Харт

2
Стаття IN як еквівалент АБО стосується списків параметрів / значень, але не для підзапитів, які в основному трактуються як об'єднання.
Ерік Харт

42

Ну, я вважаю, це "старе, але золото" питання. Відповідь: "Це залежить!". Виступи є настільки делікатною темою, що було б занадто нерозумно сказати: "Ніколи не використовуйте підзапити, завжди приєднуйтесь". У наступних посиланнях ви знайдете основні найкращі практики, які я вважаю дуже корисними:

У мене є таблиця з 50000 елементами, результат, який я шукав, - 739 елементів.

Спочатку мій запит:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

і на виконання було потрібно 7,9 секунди.

Нарешті, мій запит:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

і це зайняло 0,0256 с

Гарний SQL, хороший.


3
Цікаво, чи можете ви пояснити, як додавання групи GROUP BY виправлено?
cozos

6
Тимчасова таблиця, породжена підзапитом, була меншою. Тому виконання відбувається швидше, оскільки для реєстрації є менше даних.
Sirmyself

2
Я думаю, що в першому запиті ви поділили змінну між зовнішнім запитом і підзапитом, тому для кожного рядка в основному запиті виконується підзапит, але в другому підзапрос виконується лише один раз, і таким чином покращується продуктивність.
Алі Фараджпур

1
Сервер Sql, MySql і ... Sql (крім NoSql) настільки схожі в інфраструктурі. У нас є своєрідна система оптимізації запитів, під якою перетворюються пункти IN (...) для приєднання (якщо це було можливо). Але коли у вас є група на колоні, що добре індексується (виходячи з її кардинальності), вона буде набагато швидшою. Тож це дійсно залежить від ситуації.
Алікс

10

Почніть переглядати плани виконання, щоб побачити відмінності в тому, як сервер SQl інтерпретуватиме їх. Ви також можете використовувати Profiler, щоб реально виконувати запити кілька разів та отримувати різницю.

Я б не очікував, що вони будуть настільки жахливо різними, де ви можете отримати реальні, великі підвищення продуктивності використання об'єднань замість підзапитів, коли ви використовуєте корельовані підзапити.

EXISTS часто кращий, ніж будь-який із цих двох, і коли ви говорите, лівий приєднується там, де ви хочете, щоб усі записи були не в лівій таблиці приєднання, тоді NOT EXISTS - це набагато кращий вибір.


9

Продуктивність ґрунтується на кількості даних, які ви виконуєте на ...

Якщо менше даних, то близько 20 к. ПРИЄДНАЙТЕ краще.

Якщо дані більше схожі на 100k +, то IN працює краще.

Якщо вам не потрібні дані з іншої таблиці, IN - це добре, але краще завжди піти на ІСЦІ.

Усі ці критерії, які я перевірив, і в таблицях мають належні індекси.


4

Виконання повинно бути однаковим; набагато важливіше, щоб правильні індекси та кластеризація були застосовані у ваших таблицях ( на цій темі є кілька хороших ресурсів ).

(Відредаговано для відображення оновленого питання)


4

Два запити можуть бути не семантично еквівалентними. Якщо працівник працює в більш ніж одному відділі (можливо, на підприємстві, в якому я працюю; правда, це означає, що ваша таблиця не нормалізується повністю), тоді перший запит повертає повторювані рядки, тоді як другий запит не буде. Щоб зробити запити еквівалентними в цьому випадку, DISTINCTдо SELECTпункту повинно бути додано ключове слово , що може вплинути на ефективність.

Зауважте, що існує правило дизайну, яке визначає, що таблиця повинна моделювати сутність / клас або зв'язок між сутностями / класами, але не обидва. Тому я пропоную вам створити третю таблицю, скажімо OrgChart, для моделювання відносин між працівниками та відділами.


4

Я знаю, що це старий пост, але я вважаю, що це дуже важлива тема, особливо в наші дні, коли ми маємо записи 10M + і говоримо про терабайти даних.

Я також зважуюсь на наступні спостереження. У мене в таблиці близько 45 млн записів ([дані]) і близько 300 записів у таблиці [коти]. У мене є широка індексація для всіх запитів, про які я говорю.

Розглянемо приклад 1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

порівняно з прикладом 2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

Приклад 1 запустив близько 23 хвилин. Приклад 2 займав близько 5 хв.

Тож я б зробив висновок, що підзапит у цьому випадку відбувається набагато швидше. Звичайно, майте на увазі, що я використовую M.2 SSD-накопичувачі, здатні ввімкнути 1 Гб / сек (цей байт не є бітами), тому мої індекси теж дуже швидкі. Тож це може вплинути на швидкість теж у ваших обставинах

Якщо це одноразове очищення даних, мабуть, найкраще просто залишити його запуск і закінчити. Я використовую TOP (10000) і дивлюся, скільки часу займає і помножую на кількість записів, перш ніж потрапити на великий запит.

Якщо ви оптимізуєте виробничі бази даних, я настійно пропоную попередню обробку даних, тобто використовувати тригери або брокер для асинхронізації записів оновлення, щоб доступ у реальному часі отримував статичні дані.


0

Ви можете використовувати пояснювальний план, щоб отримати об'єктивну відповідь.

Для вашої проблеми фільтр Exists , ймовірно, виконає найшвидше.


2
"Фільтр Exists, ймовірно, виконає найшвидший", - мабуть, я не думаю, хоча для остаточної відповіді потрібне тестування на фактичні дані. Існуючі фільтри, ймовірно, будуть швидшими там, де є кілька рядків з однаковими значеннями пошуку - тому існуючий фільтр може працювати швидше, якби запит перевіряв, чи були записані інші співробітники з того самого відділу, але, ймовірно, не, коли шукати проти відділу стіл.

Чи буде він протікати повільніше в тому останньому сценарії?
Снексе

Це залежало б від оптимізатора - за певних обставин це може, але, як правило, я б очікував дуже подібних показників.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.