Что является более эффективным, предложение where или объединение с миллионами таблиц строк?

17

У нас есть веб-сайт с 250-миллиметровыми строками в одной таблице, а в другой, к которой мы присоединяемся, для большинства запросов чуть менее 15-миллиметровых строк.

Примерные структуры:

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

Нам регулярно приходится делать несколько запросов ко всем этим таблицам. Одним из них является сбор статистики для бесплатных пользователей (~ 10 тыс. Бесплатных пользователей).

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
Какая база данных и версия?
Ли Риффель
1
ты пробовал оба пути?
Гбн
Если бы это был Oracle, я бы создал индекс на основе функций для UserTable на NVL2 (Role, NULL, ID), но это похоже на другую БД.
Ли Риффель

Ответы:

20

Для современных СУБД нет никакой разницы между «явным СОЕДИНЕНИЕМ» и «СОЕДИНЕНИЕМ В ГДЕ» (если все СОЕДИНЕНИЯ являются ВНУТРЕННИМИ) в отношении производительности и плана запросов.

Явный синтаксис JOIN более четкий и менее двусмысленный (см. Ссылки ниже)

Теперь JOIN-before-WHERE - это логическая обработка, а не фактическая обработка, и современные оптимизаторы достаточно умны, чтобы это реализовать.

Ваша проблема здесь, скорее всего, индексация.

Пожалуйста, покажите нам все индексы и ключи в этих таблицах. И планы запросов

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

ГБН
источник
2
НЕ ВСЕГДА ИСТИНА, что нет разницы между joinи whereпредложением. Я оптимизирую длительные запросы все время, и иногда запросы с использованием whereпредложения работают лучше, чем те, которые используются joinв 70 раз. Если бы это было так просто и понятно, жизнь была бы всеми радугами и единорогами. И это не о каком-то древнем неясном движке - сейчас я смотрю на 70-кратное преимущество whereпредложения в SQL 2012.
ajeh
Более того, я часто наблюдаю одинаковые планы обоих подходов, и отдельные запросы выполняют одинаково, но когда whereзапрос предложения выполняется в большом пакете, частью которого он должен быть, он значительно опережает joinзапрос. Запросы SQL не выполняются в вакууме - на них влияет остальная часть полезной нагрузки сервера, и часто whereзапросы запросов выполняются довольно хорошо, что раздражает, поскольку joinсинтаксис действительно намного чище.
ajeh
3
@ajeh: Я бы предположил, что ваш опыт очень нетипичен. У вас больше проблем с запросами, если у вас есть различия в x70: это так просто
gbn
5

Вы должны рефакторинг запроса в целом

Попробуйте выполнить предложения WHERE ранее, а потом - СОЕДИНЕНИЯ

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;

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

Я получил эту идею из этого видео на 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 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Поскольку я являюсь администратором баз данных MySQL, мой метод ведения дел не требует доверия к оптимизатору запросов MySQL через позитивный пессимизм и консервативность. Таким образом, я попытаюсь реорганизовать запрос или создать необходимые покрывающие индексы, чтобы избавиться от скрытых вредных привычек MySQL Query Optimizer. Ответ @ gbn кажется более полным в том, что SQL Server может иметь больше «здравомыслия» при оценке запросов.

RolandoMySQLDBA
источник
0

У нас была [Детальная] таблица около 75M строк; таблица [Master] около 400 тыс. строк и связанная таблица [Item], в которой было 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.

СУБД была Teradata с ее технологией MPP и IDR, какова была схема индексации. Таблица из 7 строк не имела индекса, так как TABLE SCAN всегда показывал лучшие результаты.

Тимоти Олеари
источник