Как устранить дублирование строк при программном заполнении диаграммы Ганта в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

Заполнение диаграммы Ганта — задача, часто встречающаяся при разработке систем бронирования, планирования производства или учета занятости ресурсов. Одной из наиболее распространенных проблем является появление «фантомных» или дублирующихся строк, которые визуально загромождают диаграмму и искажают данные. В этой статье мы разберем по шагам, почему возникают дубли, как правильно организовать выборку данных и оптимизировать код для работы с объектом ДиаграммаГанта.

Анализ исходной задачи и структуры данных

Рассмотрим ситуацию на примере учета загруженности номерного фонда. У нас есть справочник Номера (основные объекты), подчиненный справочник Комнаты (вложенные объекты) и регистр сведений ЗагруженностьНФ, где хранятся периоды проживания. Наша цель — построить иерархическую диаграмму, где точками первого уровня будут номера, а точками второго уровня — конкретные комнаты, в которых отображаются интервалы занятости.

Типичная ошибка начинающего разработчика заключается в использовании вложенных запросов внутри цикла. Проанализируем фрагмент кода, который приводит к проблеме:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    Номера.Ссылка КАК Ссылка,
|    ЗагруженностьНФ.ДатаЗаселения КАК ДатаЗаселения,
|    ЗагруженностьНФ.ДатаВыезда КАК ДатаВыезда
|ИЗ
|    Справочник.Номера КАК Номера
|        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗагруженностьНФ КАК ЗагруженностьНФ
|        ПО Номера.Ссылка = ЗагруженностьНФ.НомерПроживания";

РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

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

Почему возникают дубли в иерархии?

Выясним основную причину дублирования. При использовании метода ТочкаНомер.Точки.Добавить() в цикле, система каждый раз создает новый объект точки подчиненного уровня. Если ваш запрос из-за ЛЕВОЕ СОЕДИНЕНИЕ возвращает несколько строк на одну и ту же комнату (например, если у комнаты есть два периода проживания), то при каждом проходе цикла будет добавляться новая визуальная строка в диаграмму.

Кроме того, использование секции ИТОГИ ПО добавляет в результат запроса агрегирующие строки. Если обход выборки настроен неверно, вы рискуете вывести сначала группировочную строку (с пустыми датами), а затем детальные строки, что также приведет к визуальному дублированию или пустым интервалам.

Решение проблемы: Правильная структура выборки

Чтобы избавиться от дублей, нам необходимо выполнить два условия:

  1. Избегать «запроса в цикле». Это не только замедляет работу, но и усложняет контроль над выборкой. Весь набор данных лучше получить одним запросом с правильной иерархией.
  2. Проверять наличие точки перед добавлением. Вместо безусловного Добавить(), следует использовать механизмы поиска или корректно обходить иерархическую выборку.

Рассмотрим исправленный подход с использованием иерархического обхода. Мы можем получить данные по всем номерам и комнатам сразу, а затем использовать ОбходРезультатаЗапроса.ПоГруппировкам.


// Пример правильного обхода иерархии без дублей
ВыборкаВладелец = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаВладелец.Следующий() Цикл
    // Устанавливаем основную точку (Номер)
    ТочкаНомер = ДиаграммаГанта.УстановитьТочку(ВыборкаВладелец.НомерСсылка);
    
    ВыборкаКомната = ВыборкаВладелец.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаКомната.Следующий() Цикл
        // Вместо Добавить() используем УстановитьТочку для иерархии, 
        // чтобы найти существующую или создать одну уникальную
        ТочкаКомната = ТочкаНомер.Точки.Добавить(); 
        ТочкаКомната.Текст = Строка(ВыборкаКомната.КомнатаСсылка);
        ТочкаКомната.Значение = ВыборкаКомната.КомнатаСсылка;
        
        // Получаем значение (пересечение Точки и Серии)
        Значение = ДиаграммаГанта.ПолучитьЗначение(ТочкаКомната, Серия);
        
        // Теперь выбираем детальные записи (интервалы) для этой комнаты
        ВыборкаИнтервалов = ВыборкаКомната.Выбрать();
        Пока ВыборкаИнтервалов.Следующий() Цикл
            Если ЗначениеЗаполнено(ВыборкаИнтервалов.ДатаНачала) Тогда
                НовыйИнтервал = Значение.Добавить();
                НовыйИнтервал.Начало = ВыборкаИнтервалов.ДатаНачала;
                НовыйИнтервал.Конец = ВыборкаИнтервалов.ДатаОкончания;
                НовыйИнтервал.Цвет = WebЦвета.НейтральноАквамариновый;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Оптимизация работы с диаграммой Ганта

Проанализируем несколько важных аспектов, которые помогут сделать работу с диаграммой более стабильной и быстрой: 1. Отключение обновления. При программном заполнении большого количества точек и интервалов обязательно используйте свойство Обновление. Это предотвратит перерисовку диаграммы после каждой добавленной строки.


ДиаграммаГанта.Обновление = Ложь;
// ... Ваш код заполнения ...
ДиаграммаГанта.Обновление = Истина;

2. Использование метода Найти(). Если вы строите сложную структуру данных, вместо Добавить() можно использовать метод Найти() по коллекции точек. Это гарантирует, что для одного и того же объекта 1С не будет создано две разные строки на графике.

3. Группировка данных в запросе. Чтобы избежать наложения интервалов друг на друга (когда в одну точку попадают идентичные записи из регистра), используйте ключевое слово РАЗЛИЧНЫЕ в тексте запроса или предварительно сворачивайте данные в таблицу значений.

Резюме

Подводя итог, можно сказать, что дублирование в диаграмме Ганта чаще всего является следствием нарушения логики обхода выборок запроса. Помните, что ЛЕВОЕ СОЕДИНЕНИЕ увеличивает количество строк в результате пропорционально количеству записей в присоединяемой таблице. Использование иерархического обхода (ПоГруппировкам) и перенос запросов из цикла в основное тело процедуры позволяют полностью решить проблему дублей и значительно повысить производительность вашего решения.

← На главную