Що є більш ефективним, пункт де або з'єднання з мільйонами плюс таблиці рядків?


17

Ми запускаємо веб-сайт, який містить 250 рядків в одній таблиці, а в іншій таблиці, до якої ми приєднуємось для більшості запитів, має трохи менше 15 ММ рядків.

Зразки структур:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Нам регулярно доводиться робити кілька запитів щодо всіх цих таблиць. Один - це захоплення статистики для вільних користувачів (~ 10k безкоштовних користувачів).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

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

У цьому випадку було б розумніше використовувати wheres замість приєднання або, можливо where column in(...),?


1
Яка база даних та версія?
Лі Риффер

2
ви пробували обидва способи?
gbn

Якби це Oracle, я створив би індекс, заснований на функції для UserTable в NVL2 (Role, NULL, ID), але це виглядає як інша БД.
Лей Ріффер

Відповіді:


20

Для сучасних RDBMS немає різниці між "явним ПРИЄДНАЙТЕ" та "ПРИЄДНУЙТЕСЬ у ТОГО, ЩО" (якщо всі ПРИЄДНУЮТЬСЯ ІННЕР) щодо плану продуктивності та запитів.

Явний синтаксис JOIN чіткіший і менш неоднозначний (див. Посилання нижче)

Тепер, ПРИЄДНАЙТЕСЬ перед тим, ЩО є логічною обробкою, а не фактичною обробкою, а сучасні оптимізатори досить розумні, щоб усвідомити це.

Ваша проблема тут, швидше за все, - індексація.

Будь ласка, покажіть нам усі показники та ключі в цих таблицях. І план запитів

Примітка: це питання було б закрито на StackOverflow, оскільки воно було дублікатом на даний момент ... COUNT (1) проти COUNT (*) - це ще один міф, що розбився.


2
НІКОЛИ ПРАВИЛЬНО, що різниці між joinі whereпунктом немає. Я постійно оптимізую тривалі запити, а іноді запити, що використовують whereпункт, працюють краще, ніж ті, що використовують joinкоефіцієнт до 70x. Якби це було просто і прямо, життя було б усіма веселками і єдинорогами. І це не про якийсь давній незрозумілий двигун - саме зараз я where
дивлюсь на 70-кратну

Крім того, я часто спостерігаю однакові плани з обох підходів і виділяють запити, які виконують абсолютно однакові, але коли whereзапит із пунктом працює у великій партії, він повинен бути частиною, він перевершує joinзапит з величезною маржею. Запити SQL не виконуються у вакуумі - на них впливає решта корисного навантаження сервера, і часто whereзапити на фразу досить добре спрацьовують, що неприємно, оскільки joinсинтаксис справді набагато чистіший.
ajeh

3
@ajeh: Я б припустив, що ваш досвід дуже нетиповий. У вас виникають більші проблеми із запитами, якщо у вас відмінності x70: це все просто
gbn

5

Ви повинні повністю змінити запит

Спробуйте виконати пункти WHERE раніше, а JOIN - пізніше

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Навіть якщо ви запустили план ПОЯСНЕННЯ для цього реконструйованого запиту, і виглядає гірше, що ваш оригінал, спробуйте його все одно. Створені внутрішньо таблиці темп виконують декартові приєднання, але ті таблиці менше для роботи.

Цю ідею я отримав із цього відео на YouTube .

Я спробував принципи з відео в дуже складному питанні в StackOverflow і отримав щедрості на 200 балів.

@gbn згадав про переконання, що у вас є правильні індекси. У цьому випадку індексуйте створений стовпець у MasterTable.

Спробувати !!!

ОНОВЛЕННЯ 2011-06-24 22:31 EDT

Ви повинні запустити ці запити:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Якщо NullRoles X 20 <AllRoles (іншими словами, якщо NullRoles менше 5% рядків таблиці), вам слід створити не унікальний індекс Ролі в UserTable. В іншому випадку буде достатньо повної таблиці UserTable, оскільки оптимізатор запитів, можливо, виключає використання індексу.

ОНОВЛЕННЯ 2011-06-25 12:40 EDT

Оскільки я DBA MySQL, мій метод виконання справ вимагає не довіряти оптимізатору запитів MySQL через позитивний песимізм та консервативність. Таким чином, я спробую перефабрикувати запит або створити необхідні індекси покриття, щоб випередити приховані шкідливі звички оптимізатора MySQL Query. Відповідь @ gbn здається більш повною тим, що SQL Server може мати більше "розумності" оцінювання запитів.


0

У нас була [детальна] таблиця приблизно 75М рядків; таблиця [Основний] приблизно 400 К рядків і пов'язана таблиця [Елемент], яка мала 7 рядків - завжди і назавжди. Він зберігав невеликий набір «Номери предметів» (1-7) і моделював паперовий бланк, мільйони якого друкувалися та розповсюджувалися щомісяця. Найшвидший запит - це той, про який ви, найменше, ймовірно, подумаєте спочатку, що передбачає використання декартової приєднання. IIRC, це було щось на кшталт:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Навіть незважаючи на те, що між [Item] та [Detail] існує логічний зв'язок "id", CROSS JOIN спрацював краще, ніж INNER JOIN.

RDBMS був Teradata зі своєю технологією MPP, а IDR - схема індексації. Таблиця з 7 рядками не мала індексу, оскільки ТАБЛИЧНЕ СКАНУВАННЯ завжди було найкращим.

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