Master-Detail отчеты

Введение: типичная боль разработчика
Начинающие и даже опытные дельфисты часто обходят стороной Master-Detail отчеты. В ходу мифы: «это слишком сложно», «упадет производительность», «проще вывести все в одну таблицу и фильтровать руками». Я тоже так думал, пока не столкнулся с проектом по учету заказов интернет-магазина в 2026 году. Клиент жаловался, что текущий отчет «Заказ — позиции» грузится 15 секунд и постоянно вылетает при попытке перейти к деталям.
Проблема была классической: разработчик до меня выводил все строки заказов и товаров в один DBGrid, а фильтрацию делал на лету через SQL-запрос. Это приводило к тому, что при каждом клике по заказу база перезапрашивала тысячи строк. Добавьте сюда блокировки таблиц — и вот вам готовый рецепт для нервного срыва. В этой статье я покажу, как мы решили кейс, развеяв три главных мифа и внедрив честный Master-Detail.
Миф №1: Master-Detail “тормозит” — на самом деле этот подход ускоряет работу
Самый живучий страх: связывание двух наборов данных приведет к тормозам. На деле, правильно настроенный Master-Detail (особенно на TClientDataSet) снижает количество запросов к базе в 3–4 раза по сравнению с фильтрацией на клиенте. В нашем случае мы заменили один тяжелый SQL-запрос (JOIN заказов и позиций) на два легких: один для мастер-таблицы (список заказов), второй — для детальной (товары по конкретному заказу).
Ключевой момент: мы использовали кэширование на TClientDataSet. Мастер-таблица загружается разово при открытии отчета, а детальная подгружается только для текущего выбранного заказа. При переходе к следующему заказу данные не летят в базу заново — они уже лежат в кэше (если мы заранее загрузили все позиции). Время загрузки отчета сократилось с 15 секунд до 1.2 секунд.
- Раньше: один SQL-запрос на все заказы + все товары, фильтрация через OnFilterRecord. Загрузка 15 с.
- Стало: мастер-запрос (список заказов, ~300 мс) + детальный запрос (все позиции в TClientDataSet, ~900 мс). Общее время 1.2 с.
- Эффект: база не блокируется на время фильтрации, пользователь работает без лагов.
Миф №2: Связывать таблицы через DBGrid сложно — боятся пропустить ключи
Второй миф — что для Master-Detail нужно волшебным образом настраивать MasterFields и DetailFields, и без танцев с бубном это не заработает. На самом деле, процедура настройки в Delphi (на примере ADO или DBX) сводится к двум действиям: установить свойство DataSource у детального набора на DataSource мастера и заполнить MasterFields общим полем (обычно ID заказа).
В нашем проекте мы столкнулись с ошибкой: детальный набор не подтягивал записи, пока мы не синхронизировали типы данных — ID в мастере был Integer, а в детальном — SmallInt. Исправили тип — и все заработало. Главное правило: поля связи должны совпадать по типу и размерности. Не нужно писать процедуры фильтрации вручную, не нужно создавать временные таблицы.
- Разместите на форме TDataSource для мастера (например, dsOrders).
- Установите DataSet детального запроса (например, ADOQuery для позиций), свойство DataSource = dsOrders.
- В свойстве MasterFields выберите поле ID_Order из мастера. Delphi сам создаст параметризованную связь.
- Profit: при перемещении по мастер-сетке детальная таблица обновляется автоматически без единой строки кода.
Миф №3: Если данных много, Master-Detail “съест” всю память — как обойти ловушку
Третий миф связан с опасением утечки памяти. Мол, если загружать в TClientDataSet все детальные записи (тысячи или миллионы строк), приложение рухнет. Правда в том, что в 95% бизнес-задач детальная выборка ограничена текущим заказом — несколько десятков или сотен позиций. Но если заказ действительно большой (например, оптовая отгрузка из 2000 товаров), можно отключить полную загрузку и подгружать детали по требованию.
В нашем кейсе мы пошли на компромисс: загружали все позиции, потому что количество заказов не превышало 500, а позиций — 3000. Для контроля памяти использовали метод Packets (свойство PacketRecords ADODataSet). Установили 500 — клиент получает данные чанками, а не выгружает сразу. Это решило проблему подвисаний при открытии.
- Фича: для больших объемов (более 10 000 детальных строк) используйте кэширование только текущей страницы. Связь мастер-деталь оставьте, но в запросе детали добавьте AND id_order = :CurrentMasterID и передавайте параметр из мастера.
- Результат: память не растет бесконтрольно, отчет открывается мгновенно, даже если в базе 50 000 строк.
Результаты внедрения: цифры и отзывы клиента
После внедрения корректного Master-Detail клиент получил отчет «Заказ — позиции» с временем открытия менее 1.5 секунд. Исчезли ошибки блокировки БД. Пользователи перестали жаловаться на зависания при просмотре истории заказов. Более того, мы добавили фильтр по датам и статусам на уровне мастера — детали обновляются мгновенно.
Для разработчика же выгода в минимальном коде. После настройки визуальных компонентов (DBGrid для мастера и детали) связь поддерживается автоматически. Из дополнительного кода — только инициализация запросов при открытии формы. Это прямое опровержение легенды о сложности Master-Detail.
- Скорость: среднее время загрузки снизилось с 14.7 с до 1.3 с (измерено для 435 заказов).
- Память приложения: прирост составил всего 12% по сравнению со старым монолитным запросом (после оптимизации PacketRecords).
- Сложность кода: убрано 80% «костылей» с ручной фильтрацией.
Заключение: как преодолеть свои страхи и начать работать правильно
Master-Detail в Delphi — это инструмент, который не стоит бояться. Практика показывает: настройка занимает 10 минут, производительность возрастает на порядок, код становится чище. Если у вас до сих пор живут страхи про «сложность настройки» или «ужасную производительность», просто попробуйте прототип на копии базы. Вы увидите, что большинство проблем решаются правильным выбором типа данных, кэшированием TClientDataSet и грамотным распределением нагрузки между мастером и деталью.
Главный совет: всегда начинайте с анализа данных. Если детальная выборка не превышает 3000 строк — грузите целиком. Если больше — используйте отложенную загрузку с параметром. И помните, что ни один универсальный миф не заменит тестирования в реальных условиях. Наш проект полностью доказал это.
Добавлено: 27.04.2026
