← Назад к вопросам

С каким максимальным количеством таблиц писал запросы в PostgreSQL

1.6 Junior🔥 71 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Максимальное количество таблиц в PostgreSQL запросах

Опыт с большими запросами

Максимальное количество таблиц — 18 таблиц в одном запросе. Это был сложный аналитический отчёт в системе управления заказами с множеством связей.

Пример: Отчёт по заказам

SELECT
    o.id as order_id,
    o.created_at,
    c.name as customer_name,
    c.email,
    u.full_name as manager_name,
    d.address as delivery_address,
    d.city,
    p.name as payment_method,
    ps.name as payment_status,
    i.quantity,
    pr.name as product_name,
    pr.sku,
    cat.name as category,
    sb.name as subcategory,
    dist.name as distributor,
    s.quantity as stock,
    w.name as warehouse,
    sh.tracking_number as shipment_tracking
FROM
    orders o                              -- 1. Основная таблица
    INNER JOIN customers c ON o.customer_id = c.id          -- 2. Клиент
    LEFT JOIN users u ON o.manager_id = u.id                -- 3. Менеджер
    INNER JOIN delivery_addresses d ON o.delivery_id = d.id -- 4. Адрес доставки
    INNER JOIN payment_methods p ON o.payment_method_id = p.id -- 5. Способ оплаты
    INNER JOIN payment_statuses ps ON o.payment_status_id = ps.id -- 6. Статус оплаты
    INNER JOIN order_items i ON o.id = i.order_id          -- 7. Позиции заказа
    INNER JOIN products pr ON i.product_id = pr.id          -- 8. Продукты
    INNER JOIN categories cat ON pr.category_id = cat.id    -- 9. Категория
    LEFT JOIN subcategories sb ON pr.subcategory_id = sb.id -- 10. Подкатегория
    INNER JOIN distributors dist ON pr.distributor_id = dist.id -- 11. Дистрибьютор
    INNER JOIN stock s ON pr.id = s.product_id AND s.warehouse_id = o.warehouse_id -- 12. Остатки
    INNER JOIN warehouses w ON s.warehouse_id = w.id        -- 13. Склад
    LEFT JOIN shipments sh ON o.id = sh.order_id            -- 14. Доставка
    LEFT JOIN shipping_routes sr ON sh.route_id = sr.id     -- 15. Маршрут
    LEFT JOIN regions r ON d.region_id = r.id               -- 16. Регион
    LEFT JOIN countries co ON r.country_id = co.id          -- 17. Страна
    LEFT JOIN discount_codes dc ON o.discount_code_id = dc.id -- 18. Скидка
WHERE
    o.created_at >= CURRENT_DATE - INTERVAL '30 days'
    AND o.payment_status_id = (SELECT id FROM payment_statuses WHERE name = 'COMPLETED')
    AND pr.is_active = true
ORDER BY
    o.created_at DESC;

Почему 18 таблиц?

Это был критический случай:

  • Сложная система с множеством сущностей
  • Нет выбора — бизнес требовал всю информацию в одном отчёте
  • Альтернативы (несколько меньших запросов) были медленнее

Проблемы, с которыми я столкнулся

1. Производительность

До оптимизации:

-- Запрос выполнялся 45 секунд!
EXPLAIN ANALYZE ...
-- Seq Scan на одной из таблиц, нет индексов

Решение:

-- Добавил индексы на внешние ключи и часто используемые фильтры
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_payment_status ON orders(payment_status_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_stock_warehouse ON stock(warehouse_id);

После: 0.5 секунд

2. Читаемость и поддержка

Проблема: 18 JOIN'ов — это кошмар для поддержки.

Решение: Разбить на VIEW'ы:

-- View для базовой информации заказа
CREATE VIEW v_order_base AS
SELECT
    o.id,
    o.created_at,
    c.name as customer_name,
    u.full_name as manager_name,
    p.name as payment_method
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
LEFT JOIN users u ON o.manager_id = u.id
INNER JOIN payment_methods p ON o.payment_method_id = p.id;

-- View для информации о доставке
CREATE VIEW v_order_delivery AS
SELECT
    o.id,
    d.address,
    w.name as warehouse,
    co.name as country
FROM orders o
INNER JOIN delivery_addresses d ON o.delivery_id = d.id
INNER JOIN warehouses w ON o.warehouse_id = w.id
LEFT JOIN regions r ON d.region_id = r.id
LEFT JOIN countries co ON r.country_id = co.id;

-- Финальный запрос (проще)
SELECT * FROM v_order_base b
JOIN v_order_delivery d ON b.id = d.id;

3. Ambiguous joins (неоднозначные соединения)

Проблема:

-- Ошибка! Из какой таблицы брать id?
SELECT id FROM orders o
JOIN customers c ON ...
JOIN distributors d ON ...;

Решение: Всегда явные префиксы:

SELECT o.id, c.id, d.id FROM orders o
JOIN customers c ON ...
JOIN distributors d ON ...;

Best Practices для больших запросов

1. Максимум JOIN'ов

Мягкий предел: 5-7 таблиц для типичного запроса Жёсткий предел: 15-20 таблиц (начинаются серьёзные проблемы)

Если больше — пересмотри архитектуру:

  • Денормализация (добавить computed поля)
  • Несколько меньших запросов
  • Materialized VIEW'ы для отчётов

2. Explain Analyze

EXPLAIN ANALYZE
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON ...
WHERE o.created_at > '2024-01-01';

-- Результат показывает:
-- - Seq Scan vs Index Scan
-- - Loops (сколько раз выполнен)
-- - Actual Time vs Planned Time

3. Оптимизация

-- ❌ Плохо: вычисляем для каждой строки
SELECT o.id, (SELECT COUNT(*) FROM order_items i WHERE i.order_id = o.id) as items_count
FROM orders o;

-- ✅ Хорошо: агрегация с JOIN
SELECT o.id, COUNT(i.id) as items_count
FROM orders o
LEFT JOIN order_items i ON o.id = i.order_id
GROUP BY o.id;

4. Пределы памяти

JOIN'ы дорогие для памяти. На PostgreSQL есть лимиты:

  • Max работа памяти на операцию: work_mem (default 4MB)
  • Если запрос требует больше — используется disk (медленно!)
# Увеличить для сложного запроса (временно)
SET work_mem = '256MB';
SELECT ... (ваш сложный запрос);
RESET work_mem;

Мой опыт с 18 таблицами

Проблемы:

  • Выполнение: 45 сек → 0.5 сек (после индексов)
  • Поддержка: 2 часа на понимание → 10 минут (после VIEW'ов)
  • Memory: Work memory увеличена до 512MB для этого запроса

Вывод: Я редко пишу такие большие запросы сейчас. Предпочитаю:

  • Несколько меньших запросов (для логики в коде)
  • Materialized VIEW'ы (для отчётов)
  • Денормализованные таблицы (для аналитики)

Это проще масштабировать, тестировать и поддерживать.