Как организовать хранение и сортировку иерархических данных в табличной части 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов на обычных формах программисты часто сталкиваются с задачей отображения иерархических структур внутри документов. Стандартная табличная часть (ТЧ) представляет собой «плоский» список, в то время как пользователю удобно работать с Деревом значений (ДЗ). Основная сложность заключается в том, как сохранить эту иерархию в базе данных так, чтобы при добавлении, перемещении или удалении строк порядок не нарушался, а в отчетах данные выводились в правильной последовательности.

В этой статье мы разберем несколько эффективных методов организации хранения иерархии, проанализируем программные способы формирования индексов сортировки и выясним, как минимизировать трудозатраты системы при перенумерации строк.

Метод 1: Программное формирование строкового шаблона (Materialized Path)

Этот метод был выбран автором темы как основной. Его суть заключается в создании служебного реквизита (например, Порядок), который хранит полный путь к узлу в виде строки с фиксированной длиной сегментов. Например: 001.002.000, где первые три цифры — индекс первого уровня, следующие три — второго и так далее.

Рассмотрим преимущества данного подхода:

  1. Простая сортировка: Стандартная строковая сортировка в запросе или ТЧ автоматически выстраивает дерево в правильном порядке.
  2. Фиксированная вложенность: Если заранее известно, что уровней будет не более трех, этот метод работает максимально быстро.
  3. Читаемость: По значению поля легко понять положение строки в структуре.

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


Функция СформироватьПорядок(Дерево, ТекущаяСтрока) Экспорт
    
    // Определяем формат сегмента (например, 3 знака с ведущими нулями)
    ФорматЧисла = "ЧЦ=3; ЧВН=";
    
    Если ТекущаяСтрока.Уровень() = 0 Тогда
        // Для верхнего уровня берем индекс строки в корне дерева
        ИндексСтроки = Дерево.Строки.Индекс(ТекущаяСтрока) + 1;
        Порядок = Формат(ИндексСтроки, ФорматЧисла) + ".000.000";
        
    ИначеЕсли ТекущаяСтрока.Уровень() = 1 Тогда
        // Для второго уровня берем индекс родителя и индекс текущей строки у родителя
        СтрокаРодитель = ТекущаяСтрока.Родитель;
        // Разрезаем порядок родителя, чтобы получить его первый сегмент
        ПорядокРодителя = СтрРазделить(СтрокаРодитель.Порядок, ".");
        
        ИндексСтроки = СтрокаРодитель.Строки.Индекс(ТекущаяСтрока) + 1;
        Порядок = ПорядокРодителя[0] + "." + Формат(ИндексСтроки, ФорматЧисла) + ".000";
        
    ИначеЕсли ТекущаяСтрока.Уровень() = 2 Тогда
        // Для третьего уровня собираем сегменты от родителя и добавляем свой
        СтрокаРодитель = ТекущаяСтрока.Родитель;
        ПорядокРодителя = СтрРазделить(СтрокаРодитель.Порядок, ".");
        
        ИндексСтроки = СтрокаРодитель.Строки.Индекс(ТекущаяСтрока) + 1;
        Порядок = ПорядокРодителя[0] + "." + ПорядокРодителя[1] + "." + Формат(ИндексСтроки, ФорматЧисла);
    КонецЕсли;
    
    Возврат Порядок;
КонецФункции

Важно отметить: при использовании этого метода необходимо обновлять индексы всех подчиненных строк при перемещении родителя или изменении его положения в списке. Однако при добавлении новой строки в конец ветки достаточно рассчитать индекс только для нее.

Метод 2: Использование Ключей связи для СКД

Если основной целью является последующий вывод данных в отчеты, мы можем использовать механизм Системы компоновки данных. В этом случае нам не обязательно хранить сложные строковые индексы. Достаточно добавить в табличную часть два реквизита: КлючСвязи (уникальный идентификатор строки, например, число или GUID) и КлючРодителя.

Проанализируем процесс настройки такого отчета:

  1. В наборе данных запроса мы выбираем все поля ТЧ.
  2. В закладке «Связи наборов данных» указываем связь набора с самим собой.
  3. В качестве условия связи выбираем: Источник = КлючСвязи, Приемник = КлючРодителя.
  4. Указываем начальное значение для корня (где КлючРодителя пустой).

СКД автоматически построит иерархическое дерево любой вложенности, независимо от физического порядка строк в документе. Это избавляет программиста от необходимости писать сложные функции пересчета индексов при каждом изменении документа.

Метод 3: Дробные индексы (Вещественные числа)

Интересной альтернативой является использование числового поля Порядок с типом Число(15, 10). Вместо того чтобы пересчитывать индексы всех соседей при вставке строки между позициями 1 и 2, мы можем просто назначить новой строке индекс 1.5. Если нужно вставить строку между 1.1 и 1.2 — назначаем 1.15.

Рассмотрим плюсы и минусы:

Метод 4: Вложенные множества (Nested Sets)

Для высоконагруженных систем, где чтение иерархии происходит гораздо чаще, чем запись, применяется алгоритм Nested Sets. Каждой строке присваиваются два индекса: ЛевоеЗначение и ПравоеЗначение.

Выясним причину сложности этого метода: при добавлении всего одного узла в начало дерева приходится обновлять значения Lft и Rgt почти у всех строк документа. Однако этот метод позволяет получить все дерево или любую его ветку одним простейшим запросом без использования рекурсии ИТОГИ ПО или иерархических связей СКД.

Оптимизация работы с табличной частью

Проанализируем техническое ограничение платформы 1С. В любой табличной части есть стандартное поле НомерСтроки. Платформа всегда стремится поддерживать его актуальность. Если ваша логическая сортировка по полю Порядок сильно отличается от физического порядка строк, это может привести к путанице при редактировании.

Рекомендуем придерживаться следующего алгоритма при сохранении документа:

  1. Перед записью (в обработчике ПередЗаписью) программно отсортируйте табличную часть по вашему служебному полю Порядок.
  2. Используйте метод ТабличнаяЧасть.Сортировать("Порядок").
  3. Это приведет физический порядок строк в соответствие с логическим деревом, что ускорит работу отчетов и сделает поведение формы предсказуемым для пользователя.

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

← На главную