Представим, что у вас есть большая таблица applications, в которой хранятся данные о заявках пользователей, а также о людях, которые их подают (они указаны в столбце
submitter_id) или рассматривают (reviewer_id). Вам нужно посчитать, со сколькими заявками взаимодействовал пользователь — неважно, отправлял или рецензировал. Какой запрос, на ваш взгляд сработает быстрее?
SELECT COUNT(*)
FROM application
WHERE submitter_id = :user_id
OR reviewer_id = :user_id;
или
SELECT (
SELECT COUNT(*) FROM application WHERE reviewer_id = :user_id
)
+ (
SELECT COUNT(*) FROM application WHERE submitter_id = :user_id
)
- (
SELECT COUNT(*) FROM application WHERE submitter_id = :user_id
AND reviewer_id = :user_id
);
Первый вариант выглядит изящнее, да и логичнее — зачем расписывать сложную конструкцию с подзапросами, когда можно обойтись 4 строками. Но при этом второй запрос выполнится почти в 100 раз быстрее. Пруф.
По той же ссылке есть объяснение, почему так получается, но если кратко:
🔵Оператор
AND уменьшает выборку данных, а индексы и статистика БД помогают оптимизировать его выполнение. Когда вам нужно отобрать данные по двум условиям, движок ищет сначала ищет записи, где выполняется более редкое условие и затем проходится по второму. 🔵Оператор
OR либо последовательно проходится по всем данным в таблице, либо целиком одной колонке, затем по второй, чтобы их объединить. Оба варианта более «дорогие», чем просто просканировать столбец и отфильтровать лишнееТак что если вы замечаете, что запросы с
OR слишком долго выполняются, то имеет смысл их переписать — пусть будут не такие красивые, зато более эффективные. Например, для кейсов, как в начале поста автор рекомендует задуматься о создании «дочерней» таблицы:CREATE TABLE application_user (
user_id int8 NOT NULL,
application_id int8 NOT NULL,
user_type enum('submitter','reviewer') NOT NULL
);
И свой изначальный запрос переделать через
JOIN:SELECT * FROM application a
JOIN application_user au USING (application_id)
WHERE au.user_id = :user_id;
Это все не повод отказываться от использования OR совсем и в любой непонятной ситуации создавать и джойнить новые таблицы. Но особенности этого оператора стоит иметь в виду, особенно, когда вы работаете с большими объемами данными.