Оптимизируйте SQL‑запрос: SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE region='EU') — как переписать и индексировать для ускорения на больших объёмах данных
Переписать запрос и проиндексировать так, чтобы СУБД делала полу‑соединение (semi-join) или использовала индекс по заказам: Варианты переписывания (рекомендуемые) - JOIN (обычно самый ясный и эффективно оптимизируемый): SELECT o.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.region = 'EU'; - EXISTS (полу‑соединение, безопасно при дубликатах и больших подзапросах): SELECT o.* FROM orders o WHERE EXISTS (SELECT 111 FROM customers c WHERE c.id = o.customer_id AND c.region = 'EU'); Индексы (обязательные / наиболее полезные) - индекс по orders.customer_id: CREATE INDEX idx_orders_customer_id ON orders(customer_id); - индекс по customers.region (лучше композиционный, чтобы ускорить выборку id по region): CREATE INDEX idx_customers_region_id ON customers(region, id); Пояснение и порядок действий - Если region селективен (малое число клиентов в 'EU'), оптимизатор сначала сканирует customers по idx_customers_region_id, затем делает быстрые lookup в orders по idx_orders_customer_id — эффективно. - Если запросы чаще сканируют orders (много заказов) и затем проверяют customers по PK, важен индекс на orders.customer_id (а customers.id обычно PK уже индексирован). - Для SELECT * покрывающие индексы невозможны; если выбираете только колонки, делайте покрывающий индекс на orders( customer_id, cols... ). - Проверяйте план: EXPLAIN / EXPLAIN ANALYZE — смотрите, используется ли index scan или hash join/merge join. - Дополнительно для очень больших объёмов: партиционирование orders (по дате или по region при денормализации), материализованный вид для популярных region, денормализация (добавить customer_region в orders и индексировать). Небольшие практические замечания - Для PostgreSQL: при добавлении индексов делайте CREATE INDEX CONCURRENTLY; можно CLUSTER по idx_orders_customer_id для ускорения диапазонных сканов. - Для MySQL/InnoDB: убедитесь, что foreign key/PK настроены и используйте ANALYZE TABLE после создания индексов. Резюме: переписать как JOIN или EXISTS и создать индексы - CREATE INDEX idx_orders_customer_id ON orders(customer_id); - CREATE INDEX idx_customers_region_id ON customers(region, id); и проверить EXPLAIN, при необходимости рассмотреть партиционирование или денормализацию.
Варианты переписывания (рекомендуемые)
- JOIN (обычно самый ясный и эффективно оптимизируемый):
SELECT o.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.region = 'EU';
- EXISTS (полу‑соединение, безопасно при дубликатах и больших подзапросах):
SELECT o.* FROM orders o WHERE EXISTS (SELECT 111 FROM customers c WHERE c.id = o.customer_id AND c.region = 'EU');
Индексы (обязательные / наиболее полезные)
- индекс по orders.customer_id:
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
- индекс по customers.region (лучше композиционный, чтобы ускорить выборку id по region):
CREATE INDEX idx_customers_region_id ON customers(region, id);
Пояснение и порядок действий
- Если region селективен (малое число клиентов в 'EU'), оптимизатор сначала сканирует customers по idx_customers_region_id, затем делает быстрые lookup в orders по idx_orders_customer_id — эффективно.
- Если запросы чаще сканируют orders (много заказов) и затем проверяют customers по PK, важен индекс на orders.customer_id (а customers.id обычно PK уже индексирован).
- Для SELECT * покрывающие индексы невозможны; если выбираете только колонки, делайте покрывающий индекс на orders( customer_id, cols... ).
- Проверяйте план: EXPLAIN / EXPLAIN ANALYZE — смотрите, используется ли index scan или hash join/merge join.
- Дополнительно для очень больших объёмов: партиционирование orders (по дате или по region при денормализации), материализованный вид для популярных region, денормализация (добавить customer_region в orders и индексировать).
Небольшие практические замечания
- Для PostgreSQL: при добавлении индексов делайте CREATE INDEX CONCURRENTLY; можно CLUSTER по idx_orders_customer_id для ускорения диапазонных сканов.
- Для MySQL/InnoDB: убедитесь, что foreign key/PK настроены и используйте ANALYZE TABLE после создания индексов.
Резюме: переписать как JOIN или EXISTS и создать индексы
- CREATE INDEX idx_orders_customer_id ON orders(customer_id);
- CREATE INDEX idx_customers_region_id ON customers(region, id);
и проверить EXPLAIN, при необходимости рассмотреть партиционирование или денормализацию.