Заполнение диаграммы Ганта — задача, часто встречающаяся при разработке систем бронирования, планирования производства или учета занятости ресурсов. Одной из наиболее распространенных проблем является появление «фантомных» или дублирующихся строк, которые визуально загромождают диаграмму и искажают данные. В этой статье мы разберем по шагам, почему возникают дубли, как правильно организовать выборку данных и оптимизировать код для работы с объектом ДиаграммаГанта.
Рассмотрим ситуацию на примере учета загруженности номерного фонда. У нас есть справочник Номера (основные объекты), подчиненный справочник Комнаты (вложенные объекты) и регистр сведений ЗагруженностьНФ, где хранятся периоды проживания. Наша цель — построить иерархическую диаграмму, где точками первого уровня будут номера, а точками второго уровня — конкретные комнаты, в которых отображаются интервалы занятости.
Типичная ошибка начинающего разработчика заключается в использовании вложенных запросов внутри цикла. Проанализируем фрагмент кода, который приводит к проблеме:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номера.Ссылка КАК Ссылка,
| ЗагруженностьНФ.ДатаЗаселения КАК ДатаЗаселения,
| ЗагруженностьНФ.ДатаВыезда КАК ДатаВыезда
|ИЗ
| Справочник.Номера КАК Номера
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗагруженностьНФ КАК ЗагруженностьНФ
| ПО Номера.Ссылка = ЗагруженностьНФ.НомерПроживания";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Установка точки верхнего уровня
ТочкаНомер = ДиаграммаГанта.УстановитьТочку(ВыборкаДетальныеЗаписи.Ссылка);
// Вложенный запрос для получения комнат (ОШИБКА: Запрос в цикле)
ЗапросКомнаты = Новый Запрос;
ЗапросКомнаты.Текст =
"ВЫБРАТЬ
| Комнаты.Ссылка КАК Ссылка,
| ЗагруженностьНФ.ДатаЗаселения КАК ДатаЗаселения,
| ЗагруженностьНФ.ДатаВыезда КАК ДатаВыезда
|ИЗ
| Справочник.Комнаты КАК Комнаты
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗагруженностьНФ КАК ЗагруженностьНФ
| ПО Комнаты.Ссылка = ЗагруженностьНФ.КомнатаПроживания
|ГДЕ
| Комнаты.Владелец = &Владелец
|ИТОГИ ПО Ссылка";
ЗапросКомнаты.УстановитьПараметр("Владелец", ВыборкаДетальныеЗаписи.Ссылка);
// ... дальнейшая обработка ...
КонецЦикла;
Выясним основную причину дублирования. При использовании метода ТочкаНомер.Точки.Добавить() в цикле, система каждый раз создает новый объект точки подчиненного уровня. Если ваш запрос из-за ЛЕВОЕ СОЕДИНЕНИЕ возвращает несколько строк на одну и ту же комнату (например, если у комнаты есть два периода проживания), то при каждом проходе цикла будет добавляться новая визуальная строка в диаграмму.
Кроме того, использование секции ИТОГИ ПО добавляет в результат запроса агрегирующие строки. Если обход выборки настроен неверно, вы рискуете вывести сначала группировочную строку (с пустыми датами), а затем детальные строки, что также приведет к визуальному дублированию или пустым интервалам.
Чтобы избавиться от дублей, нам необходимо выполнить два условия:
Добавить(), следует использовать механизмы поиска или корректно обходить иерархическую выборку.Рассмотрим исправленный подход с использованием иерархического обхода. Мы можем получить данные по всем номерам и комнатам сразу, а затем использовать ОбходРезультатаЗапроса.ПоГруппировкам.
// Пример правильного обхода иерархии без дублей
ВыборкаВладелец = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаВладелец.Следующий() Цикл
// Устанавливаем основную точку (Номер)
ТочкаНомер = ДиаграммаГанта.УстановитьТочку(ВыборкаВладелец.НомерСсылка);
ВыборкаКомната = ВыборкаВладелец.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКомната.Следующий() Цикл
// Вместо Добавить() используем УстановитьТочку для иерархии,
// чтобы найти существующую или создать одну уникальную
ТочкаКомната = ТочкаНомер.Точки.Добавить();
ТочкаКомната.Текст = Строка(ВыборкаКомната.КомнатаСсылка);
ТочкаКомната.Значение = ВыборкаКомната.КомнатаСсылка;
// Получаем значение (пересечение Точки и Серии)
Значение = ДиаграммаГанта.ПолучитьЗначение(ТочкаКомната, Серия);
// Теперь выбираем детальные записи (интервалы) для этой комнаты
ВыборкаИнтервалов = ВыборкаКомната.Выбрать();
Пока ВыборкаИнтервалов.Следующий() Цикл
Если ЗначениеЗаполнено(ВыборкаИнтервалов.ДатаНачала) Тогда
НовыйИнтервал = Значение.Добавить();
НовыйИнтервал.Начало = ВыборкаИнтервалов.ДатаНачала;
НовыйИнтервал.Конец = ВыборкаИнтервалов.ДатаОкончания;
НовыйИнтервал.Цвет = WebЦвета.НейтральноАквамариновый;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Проанализируем несколько важных аспектов, которые помогут сделать работу с диаграммой более стабильной и быстрой:
1. Отключение обновления. При программном заполнении большого количества точек и интервалов обязательно используйте свойство Обновление. Это предотвратит перерисовку диаграммы после каждой добавленной строки.
ДиаграммаГанта.Обновление = Ложь;
// ... Ваш код заполнения ...
ДиаграммаГанта.Обновление = Истина;
2. Использование метода Найти(). Если вы строите сложную структуру данных, вместо Добавить() можно использовать метод Найти() по коллекции точек. Это гарантирует, что для одного и того же объекта 1С не будет создано две разные строки на графике.
3. Группировка данных в запросе. Чтобы избежать наложения интервалов друг на друга (когда в одну точку попадают идентичные записи из регистра), используйте ключевое слово РАЗЛИЧНЫЕ в тексте запроса или предварительно сворачивайте данные в таблицу значений.
Подводя итог, можно сказать, что дублирование в диаграмме Ганта чаще всего является следствием нарушения логики обхода выборок запроса. Помните, что ЛЕВОЕ СОЕДИНЕНИЕ увеличивает количество строк в результате пропорционально количеству записей в присоединяемой таблице. Использование иерархического обхода (ПоГруппировкам) и перенос запросов из цикла в основное тело процедуры позволяют полностью решить проблему дублей и значительно повысить производительность вашего решения.