Уровни, глубина, прародители, циклы и аналоги запросом

В продолжение публикации «Транзитивное замыкание запросом» [http://infostart.ru/public/158512/] добавлены другие варианты использования того же приема. Приведены запросы для быстрого определения уровней всех элементов справочника, максимальной глубины справочника, прародителей произвольных элементов справочника, запрос для быстрого определения циклов (на примере справочника спецификаций «1С:Управление производственным предприятием») и определения множеств аналогов номенклатуры (также на примере конфигурации «1С:Управление производственным предприятием»).

В предыдущей статье был рассмотрен прием «матричного умножения» в расчете транзитивного замыкания отношений, его теоретическое обоснование и реализация на платформе «1С:Предприятие 8» на примере замыкания иерархии справочника. Из-за того, что данный прием хорошо ложится на возможности конструирования текста запроса на языке 1С, получаемый с использованием этого приема код оказывается очень компактным (всего 9 строк!) и быстрым. Возможно, у кого-то могло сложиться впечатление, что решением одной экзотической задачи с непонятным названием область применения рассмотренного метода и ограничивается. Однако, ЭТО НЕ ТАК! Существуют более приземленные практические задачи, где с большой выгодой можно применить разработанный прием построения запроса. В данной статье рассмотрены сразу пять таких задач.

1. Быстрое определение уровней всех элементов справочника одним пакетным запросом.

При использовании объектной модели для получения уровня элемента иерархического справочника можно использовать функцию «Уровень». Она показывает уровень вложенности текущего элемента справочника, при этом элементы в корне иерархии, вообще не имеющие родителей, имеют уровень «0».

У этой функции два недостатка. Во-первых, она медленно выполняется. Почему это так, понять несложно, если вспомнить, как хранится иерархия в таблицах СУБД. Во-вторых, функция «Уровень» не работает в запросе. А этого как раз очень часто и не хватает: наличие колонки, содержащей уровень иерархии элемента, упростило бы решение многих задач запросами.

Выходом может быть использование следующего запроса и построенной на нем функции

Функция УровниИерархии(ИмяСправочника, МаксимальнаяДлинаПути) Экспорт

   
Пролог = «ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ Справочник.Номенклатура
            | ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка)
            | ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ Справочник.Номенклатура;»;

   
Рефрен = «ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
            | СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
            | УНИЧТОЖИТЬ ЗамыканияДлины#1;»;

   
Эпилог = «ВЫБРАТЬ КОЛИЧЕСТВО(НачалоДуги) — 1 Предок, КонецДуги Потомок ИЗ ЗамыканияДлины#2 СГРУППИРОВАТЬ ПО КонецДуги»;

    Запрос = Новый Запрос(СтрЗаменить(Пролог, «Номенклатура», ИмяСправочника));

    МаксимальнаяДлинаЗамыканий = 1;

    Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл

        Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Рефрен, «#1», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»)), «#2», Формат(2 * МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

        МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий

    КонецЦикла;

    Запрос.Текст = Запрос.Текст + СтрЗаменить(Эпилог, «#2», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

    Возврат Запрос.Выполнить().Выгрузить()

КонецФункции

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

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

Рассмотрим, для примера, следующую иерархию номенклатуры:

Пример иерархии номенклатуры

Замыкание иерархии вернет следующую таблицу:

Замыкание иерархии

Приведенная функция на основе подсчета предков в замыкании вернет следующую таблицу:

Найденные уровни

2. Быстрое определение максимальной глубины иерархии одним пакетным запросом.

Данная задача может возникнуть, например, при выводе иерархических списков, когда требуется заранее определить  «высоту» (число этажей) отображения списка. Трудность задачи в том, что приходится просматривать все элементы справочника, для каждого из которых необходим вызов  функции «Уровень». Хотя такой код весьма прост,

Функция МаксимальныйУровеньСправочника(ИмяСправочника, Ответ = 0) Экспорт

   
Выборка = Справочники[ИмяСправочника].Выбрать();

    Пока Выборка.Следующий() Цикл Ответ = Макс(Ответ, Выборка.ПолучитьОбъект().Уровень())
    КонецЦикла;

    Возврат Ответ

КонецФункции

время его выполнения сильно и неприятно удивляет. В прилагаемой к статье обработке есть кнопка «Ой, глубина», которая вызывает написанную таким образом функцию и позволяет убедиться в большом времени ее работы. Конечно, можно использовать рекурсию, загрузив весь справочник в оперативную память, однако на больших справочниках применение рекурсии также будет не столь эффективным из-за большого количества отдельных вычислений. Пример использования рекурсии приведен здесь [http://nashe1c.ru/materials-view.jsp?id=371]. Решение не  образцовое,  однако доказывает интерес к данной теме.

В результате, наилучшим решением оказывается использование предлагаемого запроса в следующем виде:

Функция ГлубинаИерархии(ИмяСправочника, МаксимальнаяДлинаПути) Экспорт

   
Пролог = «ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ Справочник.Номенклатура
            | ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка)
            | ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ Справочник.Номенклатура;»;

   
Рефрен = «ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
            | СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
            | УНИЧТОЖИТЬ ЗамыканияДлины#1;»;

   
Эпилог = «ВЫБРАТЬ ПЕРВЫЕ 1 КОЛИЧЕСТВО(НачалоДуги) — 1 Глубина, КонецДуги Потомок ИЗ ЗамыканияДлины#2 СГРУППИРОВАТЬ ПО КонецДуги УПОРЯДОЧИТЬ ПО КОЛИЧЕСТВО(НачалоДуги) — 1 УБЫВ»;

    Запрос = Новый Запрос(СтрЗаменить(Пролог, «Номенклатура», ИмяСправочника));

    МаксимальнаяДлинаЗамыканий = 1;

    Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл

        Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Рефрен, «#1», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»)), «#2», Формат(2 * МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

        МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий

    КонецЦикла;

    Запрос.Текст = Запрос.Текст + СтрЗаменить(Эпилог, «#2», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

    Возврат Запрос.Выполнить().Выгрузить()[0].Глубина

КонецФункции

Для краткости, случай, когда в справочнике нет ни одного элемента, не рассматривается.

Для того же примера глубина будет равна 3

Найденная глубина


3. Определение прародителя (родителя верхнего уровня) в пакетном запросе.

Судя по обсуждениям на форумах, этот вопрос встречается достаточно часто. Широко известно решение, использующее итоги по иерархии [в комментарии (6) к предыдущей статье]. Однако оно не подходит для того, чтобы использоваться в середине пакетного запроса, не дает простой возможности одновременного определения родителей верхнего уровня нескольких элементов и, вероятно, не работает максимально быстро. От этих недостатков свободно следующее решение

Функция Прародители(ИмяСправочника, МаксимальнаяДлинаПути) Экспорт

   
Пролог = «ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ Справочник.Номенклатура
            | ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка)
            | ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ Справочник.Номенклатура;»;

   
Рефрен = «ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
            | СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
            | УНИЧТОЖИТЬ ЗамыканияДлины#1;»;

   
Эпилог = «ВЫБРАТЬ НачалоДуги Предок, КонецДуги Потомок ИЗ ЗамыканияДлины#2
            | ГДЕ НачалоДуги <> КонецДуги И НачалоДуги.Родитель = Значение(Справочник.Номенклатура.ПустаяСсылка)»;

   
Запрос = Новый Запрос(СтрЗаменить(Пролог, «Номенклатура», ИмяСправочника));

    МаксимальнаяДлинаЗамыканий = 1;

    Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл

        Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Рефрен, «#1», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»)), «#2», Формат(2 * МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

        МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий

    КонецЦикла;

    Запрос.Текст = Запрос.Текст + СтрЗаменить(Эпилог, «#2», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

    Возврат Запрос.Выполнить().Выгрузить()

КонецФункции

Для того же примера…

Найденные прародители

 

4. Быстрое определение циклов произвольной длины одним пакетным запросом.

Определение циклов основано на следующей идее:

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

В результате получаем следующий запрос, находящий дуги, принадлежащие циклу.

Функция ЦиклыСпецификацийУПП(МаксимальнаяДлинаПути) Экспорт

   
Пролог = «ВЫБРАТЬ Выход.Номенклатура НачалоДуги, Вход.Номенклатура КонецДуги, Выход.Ссылка ПОМЕСТИТЬ ИсходноеОтношение
            | ИЗ Справочник.СпецификацииНоменклатуры.ВыходныеИзделия КАК Выход
            | СОЕДИНЕНИЕ Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК Вход ПО Выход.Ссылка = Вход.Ссылка
            | ГДЕ Выход.Ссылка.Активная;
            | ВЫБРАТЬ РАЗЛИЧНЫЕ НачалоДуги, КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ ИсходноеОтношение
            | ОБЪЕДИНИТЬ ВЫБРАТЬ НачалоДуги, НачалоДуги ИЗ ИсходноеОтношение
            | ОБЪЕДИНИТЬ ВЫБРАТЬ КонецДуги, КонецДуги ИЗ ИсходноеОтношение;»;

   
Рефрен = «ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
            | СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
            | УНИЧТОЖИТЬ ЗамыканияДлины#1;»;

   
Эпилог = «ВЫБРАТЬ КОЛИЧЕСТВО(НачалоДуги) Уровень, КонецДуги Элемент ПОМЕСТИТЬ ТаблицаУровней ИЗ ЗамыканияДлины#2 СГРУППИРОВАТЬ ПО КонецДуги;
            | ВЫБРАТЬ ИсходноеОтношение.НачалоДуги Предок, ИсходноеОтношение.КонецДуги Потомок, ИсходноеОтношение.Ссылка Спецификация ИЗ ИсходноеОтношение
            | СОЕДИНЕНИЕ ТаблицаУровней КАК Начало ПО ИсходноеОтношение.НачалоДуги = Начало.Элемент
            | СОЕДИНЕНИЕ ТаблицаУровней КАК Конец ПО ИсходноеОтношение.КонецДуги = Конец.Элемент
            | ГДЕ Начало.Уровень = Конец.Уровень»;

   
Запрос = Новый Запрос(Пролог);

    МаксимальнаяДлинаЗамыканий = 1;

    Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл

       
Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Рефрен, «#1», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»)), «#2», Формат(2 * МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

        МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий

    КонецЦикла;

    Запрос.Текст = Запрос.Текст + СтрЗаменить(Эпилог, «#2», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

    Возврат Запрос.Выполнить().Выгрузить()

КонецФункции

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

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

Для примера приведен набор спецификаций, содержащий циклы. Это спецификация структуры всем известной детской песни «Вместе весело шагать»

Набор спецификаций с циклами

«Песенка» получается по двум спецификациям: С1(Словечко1 + Словечко2) и С2(Куплет1 + Куплет2 + Куплет3).

Функция, обнаруживающая циклы, вернет следующую таблицу

Таблица дуг циклов

 

5. Определение множества взаимозаменяемых позиций (аналогов) номенклатуры.

Существует несколько решений для хранения информации о взаимозаменяемости номенклатурных позиций [//infostart.ru/public/128065/]. Например, в УПП для этого используется регистр сведений «АналогиНоменклатуры», в записях которого указывается собственно номенклатура и заменяющий ее аналог (назначение других полей этих записей для данного обсуждения не существенно). Чаще всего можно считать, что если для детали “А” аналогом является деталь “Б”, то верно и обратное: для детали “Б” аналогом будет деталь “А”. Кроме того, если деталь “Б” является аналогом детали “А”, а деталь “В” является аналогом детали “Б”, то деталь “В” также будет аналогом детали “А”.

Как же следует задавать аналоги: по цепочке “А”->”Б”, ”Б”->”В” или звездой “А”->“Б”, ”А”->“В”? — Предлагаемый метод позволяет не задумываться об этом. В любом случае будут найдены все аналоги каждой номенклатуры. Для этого используется функция

Функция ТранзитивноеЗамыканиеАналогии(МаксимальнаяДлинаПути) Экспорт

   
Пролог = «ВЫБРАТЬ Номенклатура КАК НачалоДуги, Аналог КАК КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ РегистрСведений.АналогиНоменклатуры
            | ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ Аналог, Номенклатура ИЗ РегистрСведений.АналогиНоменклатуры
            | ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ Номенклатура, Номенклатура ИЗ РегистрСведений.АналогиНоменклатуры;»;

   
Рефрен = «ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
            | ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
            | УНИЧТОЖИТЬ ЗамыканияДлины#1;»;

   
Эпилог = «ВЫБРАТЬ НачалоДуги Предок, КонецДуги Потомок ИЗ ЗамыканияДлины#2 ГДЕ НачалоДуги <> КонецДуги»;

    Запрос = Новый Запрос(Пролог);

    МаксимальнаяДлинаЗамыканий = 1;

    Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл

        Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Рефрен, «#1», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»)), «#2», Формат(2 * МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

        МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий

    КонецЦикла;

    Запрос.Текст = Запрос.Текст + СтрЗаменить(Эпилог, «#2», Формат(МаксимальнаяДлинаЗамыканий, «ЧГ=0»));

    Возврат Запрос.Выполнить().Выгрузить()

КонецФункции

Используя данный запрос, можно существенно сэкономить на хранении информации об аналогах. То есть, вместо указания для каждой номенклатуры всех ее аналогов, можно указать основную позицию и ее заменители «звездой», либо задать аналоги по цепочке, либо пользуясь комбинацией этих подходов – в виде дерева. В результате будет храниться только «остов» отношения аналогии. Например, если в группе взаимозаменяемости 100 деталей, то по максимуму потребуется ввести 100х99 = 9900 записей типа номенклатура-аналог, а в случае хранения только основных записей потребуется хранить всего 99 записей!

На следующем рисунке приведены примеры аналогов. Красным обозначены связи, которые не хранятся в БД.

Пример аналогов

В УПП понадобится 5 записей регистра сведений «Аналоги номенклатуры» для этих связей.

Аналоги в УПП

После транзитивного замыкания аналогии будет сформирована полная таблица аналогов

Полная таблица аналогов

 

Из-за фундаментального характера затрагиваемых понятий приведенные примеры, скорее всего, не исчерпывают всего списка применений предложенного приема. Надеюсь, приведенные решения являются достаточно поучительными и послужат хорошей основой для решения и других  подобных практических задач.  

Еще двум интереснейшим задачам-примерам будет посвящена отдельная статья.

93 Comments

  1. AlexProg

    ildarovich, спасибо за статью.

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

    По поводу аналогов, они требуются для работы обычного персонала, а не математиков аналитиков. Да и интерфейс последовательной связи аналогов будет весьма замудреным. Покажите мне Вашим способом запрос, который получит конечный расход аналогов по спецификации, с учетом приоритетов, остатков аналогов в НЗП по подразделениям, всех назначенных особенностей использования (с учетом пустых значений, которые «для всех»), расчета с учетом коэффициента и единиц измерения. Не забудьте, что остатки должны списываться последовательно до нуля с учетом приоритетов. У меня например получился один пакетный запрос из 12ти запросов в 800 строк, и это, естественно, с учетом работы типовой функции разузлования спецификаций, и это только для РА, а потом пришлось создавать аналог для ПУ для 4х запросов и связывать вставки кусков с комментариями в первом запросе. Если представить весь алгоритм полностью, натянутым на ваш механизм, то пошло оно все нафиг 🙂 Я и так от этого контура ползаю крабом по столу. Рекурсия куда проще и понятней, я пожалел, что вообще взялся тогда решать эту задачу одним запросом. Не даром этот модуль 1С до сих пор оставляет в полуавтоматическом режиме (распределение аналогов при распределении материалов в спецификации).

    Это я тут наумничал к тому, что пока не будет нормальных хранимых процедур делать подобные вещи совершенно дико. Собрать текст запроса по кусочкам в случае работы с иерархией выглядит понятней, проще, а значит удобней для редактирования. Поясню, это лишь моё мнение.

    Reply
  2. iceflash

    Вот, Вот оно! Автор, молодец=) Интересная статья, интересное решение, полезные идеи/знания. А то уже как то надоели статьи о банальных вещах. Заметно, что задача проработана на «низком» уровне, начиная от реализации хранения данных в СУБД и применения к 1С.

    Больше качественного и полезного материала!

    Reply
  3. DoctorRoza

    Хорошая статья, возьму на вооружение

    Reply
  4. alekseies

    Взял на заметку! буду использоват…

    Reply
  5. ildarovich

    (1) Разузлование спецификаций номенклатуры этим методом НЕ ПОЛУЧИТСЯ в принципе. Дело в том, что в расчете разузлования потребуется АРИФМЕТИЧЕСКОЕ сложение, а не ЛОГИЧЕСКОЕ, а значит, в формуле появятся биноминальные коэффициенты, которые будут «портить» расчет количества. Один и тот же путь вхождения будет учтен несколько раз в зависимости от его длины. Тут уже был был спор с моим участием, что лучше — запрос или рекурсия в расчете разузлования. Был ранее и остаюсь сейчас сторонником текущего решения в УПП, то есть рекурсии. Тут я на той же стороне, что и Вы и нам спорить не о чем. Почему Вы решили, что речь идет о разузловании? Возможно, Вас смутили слова «определять входимость деталей в узлы и готовые изделия по их спецификациям». Здесь я понимал под входимостью (смотрел в словаре) факт вхождения (да или нет), а не количество деталей в узле. Наверное, нужно было выразиться определеннее.

    По-поводу аналогов. Пользователя не нужно пугать терминами. Ему нужно дать возможность вводить информацию в любом удобном и компактном виде, а перед использованием (или проверкой) разворачивать все аналоги, в том числе выводимые из основных. Второе, более тонкое, соображение, касается автосписания по спецификациям с учетом аналогов, приоритета, наличия и прочее. Неоднократно приходилось спорить с пользователями, которые ОШИБОЧНО считают автосписание частью автоматизации, потому что сам СЧИТАЮ АВТОСПИСАНИЕ ЗЛОМ. Вместо учета нас заставляют моделировать действительность. Обратная связь — основа управления — разрывается! Мы перестаем знать, что НА САМОМ ДЕЛЕ пошло на выпуск. Однако эти рассуждения уже не по теме данной статьи.

    Reply
  6. AlexProg

    ildarovich, ну если Вы согласны со мной, что в реальности выбранные примеры прикладного использования Вашей методологии не корректны, тогда согласитесь: примеры выбраны не удачно, или методика использоваться будет крайне редко.

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

    Reply
  7. ildarovich

    (6) Совсем не хочу соглашаться с тем, что

    в реальности выбранные примеры прикладного использования Вашей методологии не корректны … примеры выбраны не удачно, или методика использоваться будет крайне редко

    Во-первых, предлагается не методология и не методика, а прием. Примеров (пока) шесть:

    1) определение всех предков всех потомков всех элементов(транзитивное замыкание) — написать первоначальный запрос и статью меня побудили два вопроса в форуме — значит, этот вопрос интересен, а решения никто кроме меня не предложил;

    2) определение уровней всех элементов справочника — есть статья, где тот же вопрос решается гораздо более громоздким способом;

    3) максимальная глубина справочника — долгий цикл или громоздкий метод на Наше1С (автор, видимо, задачу не из пальца высосал);

    4) прародители — на мисте штук шесть веток про решение через ИТОГИ В ИЕРАРХИИ — там, что, одни теоретики собрались?

    5) циклы в спецификациях — две статьи на Инфостарте. В одной — запрос в цикле, в другой процедура до 10 уровней максимум. Откуда взята проблема? Не из практики?

    6) аналоги — посмотрите внимательно рисунок в статье: 5 записей в регистре и 14 в результате. Это реальная задача.

    Reply
  8. AlexProg

    ildarovich, не люблю я спорить. Но уж ладно, поспорю:

    1,2,3,4,5 Изначально требовалось написать один пакетный запрос, без дополнительных построений. Так что Вы задачу не решили. Конструировать запрос средствами языка можно для любой из этих задач, причем более эффективно, чем у Вас. Для каждой конкретной задачи имеется ввиду.

    6. Изначально задача туповатая. Имея лишь справочник номенклатура, невозможно пользоваться всеми преимуществами аналогового учета, как например в УПП. Просто человек не в теме.

    Некоторые на нашей памяти прикручивали ООП к 1С помница. Да, достоинств много, но нафиг никому не нужно.

    Решение у Вас красивое, умно выглядит. Но на практике для меня например совершенно не применимо.

    Больше не буду спорить, сорри 🙂

    Reply
  9. bulpi

    автору большое спасибо за

    1)Решение важных народохозяйственных задач 🙂

    2)интеллектуальное удовольствие.

    Извини, но не понял пункт 4 с песенкой. Можно ткнуть пальцем, где там цикл ? я цикла не вижу. Куплет не состоит из песенок.

    Reply
  10. kapustinag

    А по теме публикации — считаю, очень полезно. Именно потому, что древовидные запросы (задача, которая в СУБД Oracle решается штатно, т.к. в синтаксисе SQL-запросов предусмотрена специальная фраза для этого) — в 1С каждый строит сам, изобретая велосипед. Поставил бы еще плюсик-другой, если бы можно было.

    Reply
  11. iceflash

    Браво!=)

    Reply
  12. ildarovich

    (14) В каждом из трех куплетов есть слова «раз словечко, два словечко — будет песенка«. Если в этом месте вместо произнесения слова «песенка» начинать то, что она обозначает, то есть саму песенку, то дети «зациклятся».

    Reply
  13. hogik

    (0)

    Сергей (ildarovich).

    Очень хорошая и интересная статья. Как и все другие Ваши статьи по теме «Делаем запросом». Однако ни одна из них не может иметь практического применения т.к. не учитываются «особенности» многопользовательского и «интерактивного» доступа к информации.

    Ну, например, какой смысл в «заранее определить «высоту» (число этажей) отображения списка»(с), если к моменту его реального отображения «высота» может измениться?

    Или какой смысл в «определение циклов» на определенный момент состояния БД, если с общей информацией работают (исправляют/плодят циклы) несколько пользователей?

    Для решения подобных задач и предложены в 1С способы (инструмент) работы с т.н. «объектной моделью» с привлечением инструментов блокировки отдельных записей/объектов. Увы, медленно работающие т.к. реализуются, опять таки, запросами. И с очень неэффективным выполнением элементарных машинных команд обработки информации в оперативной памяти.

    Я понимаю, что в среде 1С-а приходится подбирать алгоритмы под схему базы данных, а не наоборот (как этого требует суть самой идеи БнД) — разрабатывать схему базы данных под постановку изначальной задачи. Но, думаю, даже в этих «узких» рамках можно и нужно разрабатывать алгоритмы имеющие возможность практического применение.

    P.S. Поставил «плюс» под публикацию и (1) сообщение.

    Reply
  14. ildarovich
    Reply
  15. ildarovich

    (35) И еще относительно параллелизма и блокировок. «Рассуждая на пальцах», можно представить себе любое сочетание событий. Более точным будет произвести количественную оценку. Это можно сделать с использованием моделей систем массового обслуживания (СМО). Если рабочий процесс 1 (кластера нет), то модель будет относиться к классу N/M/1. Здесь N – число пользователей, M – максимальная длина очереди (могу напутать что-то в обозначениях, заранее извиняюсь). Зная время выполнения запроса и число пользователей, максимальное время ожидания блокировки, Вы по любому учебнику по СМО определите «вероятность отказа в обслуживании». Подставив в него РЕАЛЬНОЕ время выполнения «моего» запроса, РЕАЛЬНУЮ интенсивность внесения изменений в соответствующий справочник (ну не автоматами же они генерируются), РЕАЛЬНОЕ количество одновременно работающих пользователей, Вы получите число со многими нулями – очень малую вероятность. Это ведь НСИ, она по определению меняется нечасто. Выпуск каждого нового вида продукции или изменения технологии – это целое дело (плавали, знаем). Возьмите из журнала регистрации РЕАЛЬНЫЕ данные по интенсивности работы со справочником спецификаций, контрагентов, номенклатуры для обычного предприятия! Поэтому меня больше беспокоит непонятность приема, его ненужность, большое время выполнения запроса в задачах с «неудобными» данными, а это выявит практика.

    Reply
  16. AlexProg

    (37) Вы можете написать конкретную задачу просто и ясно без философии, где бы применялся этот метод. Только не теоретическую из головы, а конкретную задачу учета? Просто: дано это это, получить то-то то-то, где ваш метод будет действительно эффективен. Может тогда я вам поверю.

    Reply
  17. hogik

    (36)

    «Получается, что решен ПРАКТИЧЕСКИЙ вопрос.»(с)

    Сергей (ildarovich).

    Нет. Предложен способ выхода из угла, в который сами себя загнали. 🙁

    В рамках 1С-возможностей совершенно реально и логичнее обеспечить, например, проверку «циклов» в процессе ввода информации. Равно как и обеспечить «высоту» для последующего её получения ОДНИМ обращение к БД. И для решения этих задач совершенно не требуется каждый раз перебирать «всю» информацию.

    Я совершенно «не против» красивых/эффективных алгоритмов обработки информации запросами. Но всему свое место. Запрос для пакетной обработки информации (отчетов). Прямой доступ для «интерактивных» многопользовательских систем. Разные задачи (цели). Разные способы (алгоритмы) решения. Разные инструменты. Разные схемы БД (даже в рамках реляционной модели!!!).

    Т.е. я об этом написал в (35) сообщении…

    P.S. Соглашусь с Вашим отнесением подобных решений к практическим, если Вы дадите описание алгоритма поиска «циклов» и ИСПРАВЛЕНИЯ «циклов» запросами в многопользовательской среде. И так, чтобы эта задача не превращалась в подобие перфокарточной/пакетной технологии середины прошлого века.

    Reply
  18. ildarovich

    (38) Могу привести, например, такой случай из практики: В начале июля этого года у нашего клиента — крупной торговой компании перестал проходить обмен УТ->БП (свои правила обмена через прямое подключение). Очень большие базы, сильно переписанная УТ10.3, автоматизированы все обмены: с импортной WMS, производством, таможней, поставщиками, филиалами и контрагентами, работа 24х7. Естественно, кластер серверов, MSSQL2008, система хранения. Разбирались 1,5 дня, задержали отчет иностранному владельцу, был скандал. Выяснилось: зацикливание справочника номенклатура! (Можете почитать: «Что бывает, когда родитель ушел в себя») Саму первопричину не нашли, ситуация никак не воспроизвелась — сейчас платформа не позволяет этого делать в принципе. Понятно, что виновато какие-то сочетание событий в обменах и в СУБД. Тем не менее для подстраховки теперь можем применять контроль зацикливания. Может быть, это не совсем такой пример, которого Вы просили, но

    Когда б вы знали, из какого сора

    Растут стихи, не ведая стыда
    Reply
  19. AlexProg

    (40) не понял, вы использовали этот механизм при обмене? Или просто для контроля зацикливания?

    Reply
  20. AlexO

    (40)

    перестал проходить обмен УТ->БП

    Выяснилось: зацикливание справочника номенклатура!

    и причем тут ваш метод, где допущена обычная для 1с-ника ошибка?

    Почему обычная? Потому что 1С «настраивает» всех своих программистов не на ПРЕДУПРЕЖДЕНИЕ и ПОИСК ошибок на этапе разработки, а исправление по факту по принципу: «Если ничего не случилось — ну и ладно».

    А по-простому — «авось, пронесет».

    Саму первопричину не нашли

    да, в 1С сделано все, чтобы было как можно запутаннее, не говоря уже об отсутствии инструментов моделирования работы обработокобменов, контроля исполнения кода и редактирования понаделанного и понаписанного.

    Reply
  21. ildarovich

    (41) Да, в этом случае, просто для контроля зацикливания

    Reply
  22. ildarovich

    (42) Уважаемый AlexO! В мире нет ничего идеального. В том числе нет идеальных программ. Например, в программном обеспечении лунной миссии Appolon после первого запуска было найдено более 100 ошибок. 1С не хуже и не лучше других платформ. Это нормальный эволюционирующий в нужную сторону инструмент для своего класса задач. Прошу понять: для меня разговор 1С — плохая — бессмысленный разговор, он ни к чему не ведет, ни к чему не побуждает. К тому же мне неудобно за Вас: получается, что за глаза Вы обижаете людей (разработчиков платформы), которых я безусловно уважаю. В программном коде платформы 8.3 около 10 миллионов строк на С++. И это (здесь я имею ввиду все версии платформы) нормально работает! Более чем у миллиона пользователей! На нескольких программных платформах, в том числе мобильных iOs, Android, с несколькими СУБД, с отказоустойчивостью и в облаках тоже.

    А мой метод (прием) при том, что он позволяет , например, в этом конкретном случае обнаружить ошибки, появившиеся в результате программного сбоя (у этого термина есть точное определение), сэкономить время и нервы большого количества людей.

    Reply
  23. AlexO

    (44)

    А мой метод (прием) при том, что он позволяет …обнаружить ошибки,

    так есть несколько способов сделать это, окромя вашего метода.

    миссии Appolon после первого запуска было найдено более 100 ошибок.

    и их не исправили, как это делают в 1С?

    В программном коде платформы 8.3 около 10 миллионов строк на С++. И это нормально работает!

    оно может на выставочных стендах и нормально работает (хотя вот у Microsoft виснет и на стендах).

    А вот в реальных базах в конце концов все тухнет и падает, хоть в 8.1, хоть в 8.2, и 8.3 — что, выделяется из этого ряда?

    которых я безусловно уважаю

    я вот … не могу уж очень сильно уважать РАЗРАБОТЧИКОВ ЯП, в котором сделано многое для запутывания разработки и усложнения интерфейса между этим самым ЯП и разработками, которые он пытается усиленно пользовать.

    Reply
  24. ildarovich

    (45)

    так есть несколько способов сделать это, окромя вашего метода.

    — опишите, пожалуйста, эти способы. Я постараюсь показать выгодные отличия.

    А вот в реальных базах в конце концов все тухнет и падает, хоть в 8.1, хоть в 8.2, и 8.3 — что, выделяется из этого ряда?

    — приведите, пожалуйста, конкретные примеры. Желательны не общие слова, а подробные технические детали.

    Reply
  25. AlexO

    (46)

    так мы еще и в старой теме обсуждали варианты поиска родителей…

    чем ваш-то метод отличается? В какую сторону? Результат-то то же 🙂

    Reply
  26. ildarovich

    (47) Поиск родителей и контроль зацикливания — это СОВЕРШЕННО разные задачи. С еще большим интересом жду ответа на вопрос

    так есть несколько способов сделать это, окромя вашего метода.
    Reply
  27. ildarovich

    (49) Смотрите, предлагается ПРИЕМ: В запросе некоторая исходная таблица соединяется сама с собой. Потом результат снова соединяется сам с собой, потом этот результат снова соединяется сам с собой и так далее. Если в исходной таблице были пути длины один, то после 10-го соединения так получатся пути длины тысяча (два в десятой степени). Если в исходной таблице в одной строке были непосредственно знакомые с друг другом люди, то во второй таблице — знакомые знакомого, в третьей — знакомые — знакомого знакомого знакомого (именно четыре!). А в десятой Вы будете рядом, даже если Вы были знакомы через тысячу посредников. Эта последняя таблица называется таблицей транзитивного замыкания.

    Ее БЫСТРЫЙ способ получения позволяет решить несколько совершенно РАЗЛИЧНЫХ практических задач (об этом вторая статья).

    Например, определить всех опосредствованных родителей любого элемента справочника. А раз всех, значит, первоначальных, я их назвал прародителями (родоначальники). Это можно сделать и другим способом, как Вы правильно заметили, но НЕ В ПАКЕТНОМ ЗАПРОСЕ и НЕ ТАК БЫСТРО как «моим» способом.

    На базе той же таблицы можно НАЙТИ ЦИКЛЫ — это совершенно другая задача. Из другой оперы, так сказать. Для этого сначала считается уровень каждого элемента. Под уровнем понимается число записей, в которых элемент справа. Далее находятся записи в исходной таблице, уровень элементов в которых одинаков. Не очевидно, но такие записи образуют цикл! А обработка их супербыстро находит!

    Поэтому очень Вас еще раз прошу ответить, какие способы Вы имели в виду, говоря

    так есть несколько способов сделать это, окромя вашего метода.
    Reply
  28. ildarovich

    (49) Статья называется

    Уровни, глубина, прародители, циклы и аналоги запросом

    Про это там и написано. Про поиск циклов — в пункте 4. Вместе СО ВСЕМ ИСХОДНЫМ КОДОМ. Больше никакого кода не требуется! Код нарочно развернут, снабжен отступами и самодокументирующими названиями переменных. Он должен быть понятен. Приведен рисунок и скриншот обработки после вызова метода для спецификаций рисунка. В комментариях к первой статье я привел получающийся текст развернутого запроса. Привел работающую обработку для того, чтобы можно было проверить метод на своих данных. Что еще я мог бы написать про этот метод, чтобы быть понятым?

    Reply
  29. AlexO

    (51)

    Про поиск циклов — в пункте 4. Вместе СО ВСЕМ ИСХОДНЫМ КОДОМ

    тогда чем код в п.4 отличается от кода поиска родителей из «Транзитивного запроса»?

    Reply
  30. ildarovich

    (52) Он отличается «прологом» и «эпилогом». Пролог отличается тем, что формируется еще одна временная таблица, запоминающая исходное отношение. Кроме того, само отношение строится из входов и выходов активных спецификаций. Эпилог отличается тем, что рассчитываются уровни, а затем исходная таблица соединяется с двумя таблицами уровней для левого и правого элемента и «режется» по условию равенства уровней. Все снабжено описанием и это прямо и ясно написано в коде.

    Reply
  31. AlexProg

    (40) пример конечно явно надуманный. В любом случае, имея сбой на уровне платформы вы обязаны избавиться от него в первую очередь, а не встраивать сервисные механизмы в горлышко. (Не знаю, что у вас там за канал). AlexO, уже написал всё. Избавится можно как на уровне платформы, так и на программном уровне, 1С отрабатывает зацикливание иерархии. Оставляя ошибку вы наживаете себе серьезный геморрой, т.е. лечите симптом.

    Я понимаю, вы бы еще привели пример с зацикливанием реквизитов, более реалистичный. Но опять же, легко решается кстати без выстраивания всей цепочки.

    Ваши разработки могут служить лишь для собирания статистики в запущенных случаях. Кому она нужна, вопрос.

    P.S. Я вспомнил один подобный случай, еще на 8.0 давно очень. Обмен явно издевался и выстраивал перепендикулярный гемор, как раз по связи владелец-реквизит. В конфигурации был очень хитроперевернуто решен механизм комплектации, который по сути повторял 7.7. того-же творца. Так вот, паренек, который на тот момент работал с 1С меньше года решил эту проблему на SQL, и за что был анально покаран. После этого, он написал триггер, который пузырьковал эту штуку. Я его реально зауважал. Вот, стремитесь. Это всё не сложно, логично и выглядит действительно круто.

    Reply
  32. AlexO

    (53)

    Он отличается «прологом» и «эпилогом».

    т.е. поиск

    ГДЕ Родитель1 = Потомок3 

    из примера ( 49) решается исключительно данным способом?

    Или — зачем такие сложности, Сергей, для решения вышеуказанной задачи? 🙂

    Reply
  33. ildarovich

    (54) Слово «надуманный» здесь никак не подходит. Это АБСОЛЮТНО РЕАЛЬНЫЙ случай. Чтобы локализовать эту проблему, требуются диагностические инструменты. Этот запрос — он и есть(еще раз повторю, это один из примеров его применения). Мы не можем позволить себе контролировать каждую запись в справочник на зацикливание, перепроверяя платформу. Это скажется на быстродействии. Поэтому контроль именно в том месте, где нужно — перед проблемной операцией. С тех пор еще много чего в профилактических целях делали, сбой не повторялся.

    Пример с зацикливанием реквизитов я привел в основной статье в пункте 4. Спасибо, что сочли его реалистичным.

    Reply
  34. AlexO

    (54) AlexProg,

    который пузырьковал эту штуку

    так обкатанные методы обработки данных не отменены нигде, кроме 1С 🙂

    Reply
  35. ildarovich

    (55) Сложность в том, что число уровней в «моем» методе ничем не ограничено. Он решает и задачу тогда когда Вам потребуется сделать ТЫСЯЧУ СОЕДИНЕНИЙ, проверяя

    Где Родитель1 = Потомок1000

    , а мне всего 10. В общем, попробуйте.

    Reply
  36. ildarovich

    (54) Если Вы все же захотите действительно разобраться в сути этого приема, то Вы поймете, что он также будет многократно ускорять и решение подобных задач на чистом SQL.

    пример с зацикливанием реквизитов … легко решается кстати без выстраивания всей цепочки.

    — попробуйте ка доказать это утверждение! — Очень хочется посмотреть!

    По прежнему жду от AlexO подтверждения того, что

    так есть несколько способов сделать это, окромя вашего метода.
    Reply
  37. AlexProg

    (59) Решение реквизитов лично я делал кучей способов.

    Например движение из двух точек к центру быстрей, чем движение от А до Б метода цепочки.

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

    И еще я бы рассмотрел анализ прямой вложенности запроса, при условии, что она работает на 10% быстрее, в случае длинных связей большой прирост производительности. Другой вопрос, где оно обломается, я думаю вы уже обсуждали этот вопрос.

    Но это философия. Если правильно архитектурно подходить, то необходимо создавать систему контроля при вводе данных и где не критична скорость, потому что проконтролировать использование таких данных, вы можете только тогда, когда сами продолжаете разработку. А это в наше время непозволительная роскошь. А вас потом вспомнят «добрым словом».

    Reply
  38. hogik

    (37)

    Сергей (ildarovich).

    Очень огорчил меня этот Ваш текст.

    Попробую привести пример:

    Имеем в базе данных иерархическую структура. Не имеет значение её прикладное назначение и «интенсивность внесения изменений»(с). По определению «задачи» в ней могут быть «циклы». Их надо искать и исправлять. Запускаем Ваш алгоритм. Получаем информацию о нескольких «циклах». Хотим исправить ошибки побыстрей. Сажаем двух операторов. И они начинают исправлять ошибки. Допустим, что самым простейшим способом — удалением «плохих» узлов. Смотрят, грубо говоря, каждый в свою «распечатку». Какова вероятность того, что один из операторов начнет исправлять уже исправленный «цикл» другим оператором? Т.е. им надо обеспечить постоянное обновление «распечатки» с учетом изменений после каждой процедуры исправления? Каким алгоритмом это следует делать? Как учесть в Вашем алгоритме тот факт, что в момент его выполнения под нужды одного оператора, другой оператор уже «прицелился» к очередному «циклу» и «нажимает» кнопку удаления?

    Reply
  39. ildarovich

    (60)

    Решение реквизитов лично я делал кучей способов

    Убедительно Вас прошу, приведите все, какие вспомните из этой «кучи»! Пока это только слова. Тогда я Вам смогу нагляднее показать, в чем практическая выгода «моего» способа.

    Reply
  40. ildarovich

    (61) «Замечено, что ОДНОВРЕМЕННОЕ нажатие четырех кнопок на клавиатуре дает более интересные эффекты, чем одновременное нажатие трех» (шутка)

    Хотим исправить ошибки побыстрей. Сажаем двух операторов.

    — просто не делайте этого! (организационное решение) «Поспешишь — людей насмешишь» (народная мудрость). Если Вам нужно организовать работу в коллективе — первым делом нужно «распределить работу». А давать задачу «на собачку-драчку» — кто быстрее найдет ошибку — это для игры, а не для серьезных дел, тем более, исправления ошибок! Кроме того, есть механизм управляемых блокировок. (программно-техническое решение). Если же у Вас специализированная система по «разрыву циклов» с большим потоком входящих требований (пока не вижу реальной задачи для такой системы) следует добавить дополнительные таблицы (справочники, регистры сведений), разбить граф на кластеры, назначить ответственных, придумать систему диспетчиризации.

    Reply
  41. ildarovich

    (1) Не хотел отвлекаться от темы, но все же было бы интересно взглянуть на Ваш запрос из 800 строк. Вы можете оформить его как обработку заполнения табличной части «Распределения материалов на выпуск». Штатный механизм не учитывает аналогов, так что будет интересно. Дело в том, что я тоже решал эту задачу. В моем запросе строк было ГОРАЗДО меньше. Правда, с темой данной статьи это не связано.

    Reply
  42. ildarovich

    (65) Опять одни слова — ни строчки кода. Значит, той разработки тоже нет. Предлагаю отложить все разговоры до того, когда Вам будет что предъявлять в доказательство своих пока безосновательных умозаключений. Пока из последовательности Ваших мыслей-слов сделал вывод, что в сути моего метода Вы не разобрались, а жаль.

    Reply
  43. ildarovich

    (67)

    да да, куплюсь я на дешевый вызов

    — это означает, что Вы представите все таки когда-нибудь наконец какие-либо доказательства?

    Мне тут соревноваться не с кем и не зачем

    — зря Вы так, соревноваться вообще полезно. Впрочем, я тоже иногда повторяю за рулем:

    Да кто такой этот Шумахер? — Он меня ни разу не обгонял!

    Пока Ваше решение — это еще совсем не решение. Его никто не видит. Но действительно интересно будет посмотреть. Матчасть учу постоянно, скрипеть зубами привычки нет.

    Reply
  44. hogik

    (63)

    «просто не делайте этого! (организационное решение)»(с)

    Сергей (ildarovich).

    Эта шутка мне больше понравилась, чем про три клавиши. 🙂

    Еще раз на пальцах.

    Запрос — это пакетная обработка информации.

    Пакетная обработка имеет ряд «характеристик» (упрощенно говоря!!!):

    1) Скорость выполнения.

    2) Количество информации «вовлеченной» в получение результата.

    3) Время актуальности конечного результата.

    Вы сосредоточились ТОЛЬКО на первом пункте.

    Но по сути ВСЕХ Ваших решений требуется выполнять «интерактивную» обработку информации на основании результатов «пакетной» обработки. Чем больше информации «вовлечено» в получение результата пакетом, тем выше вероятность потери её актуальности. И меньше времени она является актуальной.

    Для решения этой «проблемы» применяются блокировки. В Ваших алгоритмах требуется блокировка ВСЕЙ таблицы на ВСЁ время выполнения «пакета» и последующего воздействия на информацию в интерактивном режиме. Т.е. — однопользовательский режим обработки информации. И уменьшение времени выполнения «пакета» — это только «Игры разума»(с).

    Сергей. Это элементарные, очевидные вещи. Если не смотреть на задачи «узко-конкретно»(с).

    Я очень огорчен. Желаю успехов…

    Reply
  45. ildarovich

    (69)Владимир! – Меньше всего хочу, чтобы Вы огорчались. Знаете, как на флоте борются с тараканами? – Дают им напиться рассола, а потом, когда они захотят пить и вылезут из трюма, задраивают все люки. И НЕВАЖНО – УТОНУТ ОНИ ИЛИ ПОДОРВУТСЯ НА МИНАХ! Так и в моем случае – я придумал прием и написал на его основе несколько запросов для решения известных всем задач. А будут ли исправляться найденные ошибки или списываться найденные аналоги при этом неважно. Вспомните про тараканов – может быть, по ним ударит ракетный крейсер! Статья называется не «разработка высоконагруженной системы для контроля и исправления ошибок зацикливания в ориентированных графах большой размерности». Она называется «Уровни, глубина, прародители, циклы и аналоги ЗАПРОСОМ» и речь идет о ЗАПРОСЕ. Вы сами сузили метод до контроля циклов, присоединили задачу исправления ошибок, надумали ситуацию высокой интенсивности и параллелизма исправления ошибок и огорчились.

    Далее, при чем здесь платформа 1С? Прием с тем же успехом можно реализовать в виде RCTE на SQL. Возможно, если бы sql.ru был бы таким же «нарядным» как Инфостарт, я бы предлагал прием там.

    Так что не огорчайтесь! Смотрите: весна, все цветет, солнце, птички поют, кенгуру прыгают! – Радуйтесь! Вы скажете – при чем здесь Австралия! Вы притянули за уши надуманную задачу и расстроились. Я присоединил за уши Австралию и обрадовался! Чувствуете разницу? Так что больше позитива – больше креатива! Успехов и вам!

    Reply
  46. ADirks

    (ildarovich) Есть такая категория людей, которым очень нужно время от времени говорить кому-нибудь «ты тупой неудачник». И что бы ты ни написал, всё равно ты неудачник и лошара. Не обращай внимания.

    Нормальные же люди взяли на заметку, и при случае применят без криков.

    Reply
  47. DAnry

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

    Reply
  48. insurgut

    (71) ADirks, зато от таких людей есть очень хороший плюс — они постоянно побуждают тебя двигаться дальше, познавать, изучать, разрабатывать новые идеи 🙂

    Reply
  49. wildhog

    Reply
  50. wildhog

    Запрос = Новый Запрос;

    Запрос.Текст = «ВЫБРАТЬ

    | 1 КАК Ссылка

    |ИЗ

    | Справочник.Номенклатура КАК Номенклатура

    |ГДЕ

    | Номенклатура.ЭтоГруппа = Истина

    |ИТОГИ ПО

    | Номенклатура.Родитель.Ссылка ИЕРАРХИЯ»;

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

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

    МаксимальныйУровень = 0;

    МассивСтрок = Дерево.Строки.НайтиСтроки(Новый Структура(«Ссылка», NULL), Истина);

    Для каждого СтрокаДерева Из МассивСтрок Цикл

    Уровень = СтрокаДерева.Уровень();

    Если МаксимальныйУровень < Уровень Тогда

    МаксимальныйУровень = Уровень;

    КонецЕсли;

    КонецЦикла;

    МаксимальныйУровень = МаксимальныйУровень + 1;

    Reply
  51. wildhog

    Прошу прощения за (74) (75). Нажимал «просмотр» — а вышел ответ…

    Что собственно хотел сказать.

    Возникла задача определить максимальный уровень группы справочника «Номенклатура», не важно есть в ней элементы, или нет.

    В итоге вышло (75).

    У нас в справочнике «Номенклатура» — ~200К элементов, ~3,5К групп.

    На этих данных функция «ГлубинаИерархии» «работала» 2,5 минуты (база тестовая, файловая).

    Предлагаемый мной вариант — меньше 5 секунд.

    Т.е разница на порядок как минимум. Тестировать более строго пока нет возможности.

    Reply
  52. ildarovich

    (76) Вообще я не претендую на то, что предложенный метод будет всегда и во всем лучшим. Его утяжеляет то, что он на промежуточных этапах строит все возможные связи в графе. А также то, что временные таблица не индексированы. Нужно, кстати, попробовать тут соединение на основе сортировки слиянием (то есть объединить и сгруппировать) — должно получаться быстрее. Но запрос будет более громоздким, а я здесь стремился к простоте и изяществу.

    Но все же, чтобы соревнование было честным, Вам нужно поставить условие

    Номенклатура.ЭтоГруппа = Истина

    и в мой запрос тоже. В пролог.

    Пролог = «ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ Справочник.Номенклатура

    | ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка) И Ссылка.ЭтоГруппа

    | ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ Справочник.Номенклатура;»;

    Сообщите, пожалуйста, результат.

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

    Reply
  53. wildhog

    (77) Тут и соревнования то никакого быть не может. В данной задаче работа с ссылками убивает все.

    Я не собирался устраивать соревнования, только хотел поделиться придуманным мной велосипедом. На целую статью наверное маловато текста, а как комментарий/дополнение к вашей публикации — вполне.

    Попробовал ваш вариант с ограничением по группам — значительно лучше, 20с

    Reply
  54. ildarovich

    (78) ОК, понял. За результат спасибо, однако я допустил неточность, забыв про условие во второй половине запроса.

    Пролог = «ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ Справочник.Номенклатура

    | ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка) И Ссылка.ЭтоГруппа

    | ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ Справочник.Номенклатура И Ссылка.ЭтоГруппа;»;

    Также взглянул на Ваш код. Возможно, Вам понравится использовать конструкцию, которую использую в этом случае я:

    Для каждого СтрокаДерева Из МассивСтрок Цикл

    МаксимальныйУровень = МАКС(МаксимальныйУровень, СтрокаДерева.Уровень())

    КонецЦикла;

    Reply
  55. wildhog

    (79) И это правильный ответ ) 7 секунд. Только скорее всего не И Ссылка.ЭтоГруппа, а ГДЕ Ссылка.ЭтоГруппа

    Уточняю, (75) — 1 секунда.

    Про решение через МАКС спс)

    Reply
  56. Taktic

    Не могу понять что за параметр «МаксимальнаяДлинаПути» в каждой функции? Откуда он берется?

    Reply
  57. ildarovich

    (81) Это оценка максимальной длины пути в графе. Для отношения иерархии в справочнике длина пути никогда не может быть больше числа элементов, например. Но, как правило, можно определить это число «из соображений здравого смысла», установив, например, что в справочнике не будет больше 1000 уровней.

    Reply
  58. ildarovich

    Обещанные в конце статьи «две интереснейших задачи-примеры» можно посмотреть в публикации Определение кратчайших путей, критических путей одним запросом.

    Reply
  59. prodines

    Функция ГлубинаИерархии(ИмяСправочника, МаксимальнаяДлинаПути) как раз и вычисляет «МаксимальнаяДлинаПути». Тогда чему же равно значение «МаксимальнаяДлинаПути», передаваемое в эту функцию?

    ЗЫ Не заметил, что ответ приведен двумя постами выше. Но задать МаксимальнаяДлинаПути = 1000 неразумно, т.к. столь большое значение снизит производительность функции. Получается, надо «на глазок» задавать достаточно-реалистичное значение этого параметра (слегка с запасом).

    Reply
  60. teriban

    Интересная статья, даже почти разобрался 🙂

    Тут, в примере:

    Эпилог = «ВЫБРАТЬ КОЛИЧЕСТВО(НачалоДуги) — 1 Предок, КонецДуги Потомок ИЗ ЗамыканияДлины#2 СГРУППИРОВАТЬ ПО КонецДуги»;

    можно определить последний уровень, а вот можно ли как-то получить номера уровней всех групп?

    Например:

    Предок Потомок Уровень

    Группа1 | Элемент | 1

    Группа2 | Элемент | 2

    Группа3 | Элемент | 3

    Группа4 | Элемент | 4

    Reply
  61. ildarovich

    (85) Странный вопрос. Потому что код, который вами же приведен, делает именно то, что вы хотите — определяет номера уровней всех элементов (и групп, естественно тоже). Это раздел 1 статьи. Там и скриншот результата работы функции приведен, где видно, что определяются именно уровни.

    Ну а если затруднения в том, чтобы отобрать группы, то посмотрите начало запроса в комментарии (79). Там в два места добавлена проверка Ссылка.ЭтоГруппа. Таким тогда и должен быть пролог запроса.

    Reply
  62. teriban

    (86) может я что-то не понял (или не так написал) но, на мой взгляд:

    приведенный код позволяет определить уровень группы в который входит элемент. Для моей задачи нужно знать уровень группы в который входит элемент, а также уровень группы в которую входит группа в которую входит элемент 🙂 и т. д.

    Reply
  63. ildarovich

    (87) teriban, из первоначальной формулировки вашего вопроса было непонятно, но если теперь формулировка такая

    а вот можно ли как-то получить номера уровней всех групп? (включающих заданный элемент)

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

    Эпилог = «ВЫБРАТЬ УровниПредков.Уровень, УровниПредков.Предок
    |Из (ВЫБРАТЬ КОЛИЧЕСТВО(НачалоДуги) — 1 Уровень, КонецДуги Предок
    |ИЗ ЗамыканияДлины#2
    |СГРУППИРОВАТЬ ПО КонецДуги) КАК УровниПредков
    |СОЕДИНЕНИЕ ЗамыканияДлины#2 КАК Связи
    |ПО УровниПредков.Предок = Связи.НачалоДуги
    |И Связи.КонецДуги = &ЗаданныйЭлемент»;

    …как-то так…

    Reply
  64. kentavr27

    (0) Замечательные алгоритмы, за которые огромное спасибо.

    Только вот требуется помощь. Долгое время уже кручу и так и сяк, не могу найти нормального решения. Может подмогнете?

    Исходные данные:

    Иерархический справочник с подчинением элементам. Глубина неизвестна (предположим до 10 уровней). Длина веток может быть любой.

    Задача

    Получить Список элементов справочника с заданным родителем. В выборку должны попасть только те элементы у которых хотя бы один потомок, находящийся на самом нижнем уровне удовлетворяет условию типа Элемент.Свойство=&МоеСвойство

    Reply
  65. ildarovich
    Reply
  66. kentavr27
    Reply
  67. ildarovich

    (91) kentavr27, ну да, то, что свойство будет у самого нижнего уровня, здесь никак не проверяется. Нужна еще одна проверка для «конца дуги» при формировании таблицы «содержательная».

    ВЫБРАТЬ РАЗЛИЧНЫЕ
    ЗамыканияДлины4.НачалоДуги КАК Элемент
    ПОМЕСТИТЬ Содержательные
    ИЗ
    ЗамыканияДлины4 КАК ЗамыканияДлины4
    ГДЕ
    ЗамыканияДлины4.КонецДуги.МетодНачисления = &МетодНачисления
    И ЗамыканияДлины4.КонецДуги В (ВЫБРАТЬ НачалоДуги
    ИЗ ЗамыканияДлины1
    СГРУППИРОВАТЬ ПО НачалоДуги
    ИМЕЮЩИЕ (Количество(*) = 1))
    ;

    Показать

    Код нужно еще проверить, но идея в том, что у «листьев» только одна исходящая дуга (замкнутая на себя)

    Reply
  68. kentavr27

    (92) Да. То что нужно. Спасибо огромное. Осталось терь в голове это все упорядочить… 🙂

    Reply
  69. DeniNikitin

    Для УПП 1.3.64.1 циклы не работают!!!

    Reply
  70. DeniNikitin

    Вот пример иерархии в справочнике спецификаций и сама спецификация

    Reply
  71. ildarovich

    (94) (95) DeniNikitin, уточните, пожалуйста, как именно не работают, будем разбираться:

    — ошибка выполнения? — тогда скриншрт пришлите

    — не находит циклы? — тогда желателен скриншот не одной спецификации, а всех, входящих в цикл.

    Reply
  72. DeniNikitin

    (96)

    Не заполняет вообще, скрин спецификаций из вашего запроса прикладываю или какой скрин вам нужен?

    Reply
  73. DeniNikitin

    Ещё прикладываю файл excel со всем списком спецификаций из справочника!

    Reply
  74. ildarovich

    (98) DeniNikitin, спасибо, попробую разобраться на следующей неделе, но вы уверены, что в этом наборе спецификаций есть циклы активных спецификаций?

    Reply
  75. DeniNikitin

    (99)

    По подробней как это проверить?

    Reply
  76. DeniNikitin

    Если вы про то что изделие состоит из другиз изделий, то да! Например: Кисть состоит из бугеля и щетины, а бугель состоит из подшипника и ручки и.т.д.!

    Reply
  77. ildarovich

    (101) DeniNikitin, назначение функции, приведенной в разделе 4 — определение ЦИКЛОВ в наборе активных спецификаций. Наличие циклов — это ОШИБКА, допущенная при вводе спецификаций. Эта ошибка приводит к зацикливанию расчетов потребностей в материалах в процессе разузлования или неточностям этих расчетов.

    Цикл — это ситуация, когда

    Кисть состоит из бугеля и щетины, а бугель состоит из подшипника и ручки и.т.д.!

    где в «и т.д.» есть спецификация, где ручка состоит из кисти (ошибочно), в результате образуется цикл через несколько спецификаций.

    В большом наборе спецификаций обнаружить ошибку в виде цикла трудно. Решение из пункта 4 упрощает решение этой задачи. И если на вашем наборе данных обработка не находит циклов (ошибок), возможно, значит, что циклов (ошибок) в вашем наборе нет.

    Reply
  78. DeniNikitin

    (102)

    Тогда понятно, я просто думал, что это функция позволить мне вернуть таблицу значений разобранную и по ней уже работать, а так всё равно писать надо будет!

    Reply
  79. Apatic

    Спасибо за статьи!

    Очень интересно, действительно впечатляет.

    А насчет максимальной глубины справочника, я, конечно, чувствую себя жутким быдлокодером, но всегда использовал что-то типа такого решения:

    ВЫБРАТЬ

    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура.Ссылка) КАК Ссылка

    ИЗ

    Справочник.Номенклатура КАК Номенклатура

    ГДЕ

    Номенклатура.Родитель…n….Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

    Минус метода, что надо знать примерно возможную верхнюю границу количества родителей. Если с ней получаем 0, то затем количество родителей уменьшаем n на один до тех пор, пока результат запроса не станет равен 0.

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

    Насчет быстродействия не знаю, но для моих задач всегда быстро срабатывало.

    Reply
  80. hornet_X

    ildarovich,

    спасибо огромное за статью)))

    Reply
  81. Chamlay_Mikola

    Большое спасибо!

    Reply
  82. Synoecium

    (104)


    ВЫБРАТЬ

    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура.Ссылка) КАК Ссылка

    ИЗ

    Справочник.Номенклатура КАК Номенклатура

    ГДЕ

    Номенклатура.Родитель…n….Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

    извиняюсь за некропостинг, но такой метод можно ускорить с помощью половинного деления. Тогда в худшем случае потребуется не N раз выполнять запрос, а только log2n раз. (берем стартовое значение, например 10, если взяли мало, то умножаем на 2, если много, то делим пополам отрезок и т.д.)

    Reply
  83. ildarovich

    (107) Сомневаюсь, что так можно добиться ускорения. Дело в том, что приведенный запрос, который вы хотите поместить внутрь цикла подбора значения уровня, сам по себе не быстрый. За счет соединения через точку Родитель…Родитель…Родитель для каждого элемента справочника выполняется n неявных соединений, где n — пробная глубина справочника.

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

    Вообще выигрыш (при использовании подхода, изложенного в статье) будет чуть меньше n/log2(n), так как потребуется время на запись элементов временных таблиц.

    Reply
  84. Synoecium

    (108)

    На самом деле такой запрос очень быстрый, так как он транслируется в запрос SQL с соединениями по индексированному полю Ссылка. В базе с количеством элементов номенклатуры около 112 тысяч, запрос с 7-ю обращениями к родителю выполняется за 0,3 сек. Причем при количестве соединений больше максимальной глубины иерархии, время выполнения запроса почти не увеличивается. Можете сами попробовать.

    SELECT CAST(COUNT(DISTINCT T1.Ссылка) AS NUMERIC(10))
    FROM Справочник.Номенклатура T1
    LEFT OUTER JOIN Справочник.Номенклатура T2 ON T1.Родитель = T2.Ссылка
    LEFT OUTER JOIN Справочник.Номенклатура T3 ON T2.Родитель = T3.Ссылка
    LEFT OUTER JOIN Справочник.Номенклатура T4 ON T3.Родитель = T4.Ссылка
    LEFT OUTER JOIN Справочник.Номенклатура T5 ON T4.Родитель = T5.Ссылка
    LEFT OUTER JOIN Справочник.Номенклатура T6 ON T5.Родитель = T6.Ссылка
    WHERE (T6.Родитель = @P1)
    

    Показать

    Reply
  85. ignor

    Статья интересная. Однако моя попытка перепилить для документов в ЗУПе мне не удалась. В ЗУПе есть такой нюанс с иерархией Документов. Ряд документов имеют ссылку на предка в «ИсправляемыйДокумент» или в «ПерерассчитываемыйДокумент». Очень хочется получить иерархию исправлений и пересчетов от первого откорректирораванного документа, то есть от документа на который есть ссылка в реквизите

    Reply
  86. ildarovich

    (110) А что конкретно не получилось?

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

    То есть метод рассчитан все же на выявление длинных связей среди относительно небольшого количества объектов (до нескольких тысяч). И отличается тем, что в процессе работы устанавливает (заодно) все связи, даже те, которые впоследствии могут и не понадобиться.

    Reply
  87. eden

    Спасибо за публикацию.

    Метод нашел ПРАКТИЧЕСКОЕ применение в реальной задаче — поиск настройки номенклатуры на ближайшем ее родителе.

    Проблема оперирования со всей таблицей была решена усложнением пролога, сдесь он получает все группы таблицы и только нужные элементы.

    «ВЫБРАТЬ
    | ТаблицаБД.Родитель КАК Родитель,
    | ТаблицаБД.Ссылка КАК Ссылка,
    | ИСТИНА КАК СсылкаЭтоГруппа
    |ПОМЕСТИТЬ ДугиДлины_1
    |ИЗ
    | &ИмяТаблицыБД КАК ТаблицаБД
    |ГДЕ
    | ТаблицаБД.ЭтоГруппа
    | И НЕ ТаблицаБД.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
    |
    |ОБЪЕДИНИТЬ
    |
    |ВЫБРАТЬ
    | ТаблицаБД.Ссылка,
    | ТаблицаБД.Ссылка,
    | ИСТИНА
    |ИЗ
    | &ИмяТаблицыБД КАК ТаблицаБД
    |ГДЕ
    | ТаблицаБД.ЭтоГруппа
    |
    |ОБЪЕДИНИТЬ
    |
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    | ИсходнаяТаблица.Ссылка.Родитель,
    | ИсходнаяТаблица.Ссылка,
    | ЛОЖЬ
    |ИЗ
    | &ИмяТаблицыОбрабатываемыхДанных КАК ИсходнаяТаблица
    |ГДЕ
    | НЕ ИсходнаяТаблица.Ссылка.ЭтоГруппа
    | И НЕ ИсходнаяТаблица.Ссылка.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
    | И &ТекстЗапросаУсловие
    |
    |ОБЪЕДИНИТЬ
    |
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    | ИсходнаяТаблица.Ссылка,
    | ИсходнаяТаблица.Ссылка,
    | ЛОЖЬ
    |ИЗ
    | &ИмяТаблицыОбрабатываемыхДанных КАК ИсходнаяТаблица
    |ГДЕ
    | НЕ ИсходнаяТаблица.Ссылка.ЭтоГруппа
    | И &ТекстЗапросаУсловие»;
    

    Показать

    В эпилоге получаются уровни группировок родителей, затем все это соединяется с регистром в котором хранятся настройки и группируются по МАКСИМУМ(Уровень).

    В базе с 146тыс позиций номенклатуры и обработкой 1000 элементов с максимальной длиной 30 отрабатывает за менее 300мс.

    P.S.: Писал запрос понятным для себя языком, т.к. не имею математического образования и все эти термины мне непонятны, поэтому стиль запроса немного отличается от того что в статье. Смысл при этом не поменялся. Также добавил поле СсылкаЭтоГруппа, т.к. в эпилоге накладываю на него отборы, через точку работает медленней на 50-100 мс.

    P.P.S.: Попробовал выполнить оптимизацию запроса добавлением индексов во временные таблицы — ничего не добился, да и старался немного. SQL Profiler говорит о том, что основное время тратится на запись временной таблицы в tempdb. При этом каждая следующая таблица обрабатывает примерно за одинаковое время, т.е. увеличение максимальной длины будет добавлять в моем случае примерно 50мс к каждой новой таблице.

    Reply
  88. ildarovich

    (112) Большое спасибо за информацию, такие отзывы (о практическом применении) очень ценны для меня.

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

    Reply
  89. ignor

    (111) Отобразилась таблица с пустыми ссылками по числу документов данного вида в БД. Я же изначально искал возможность получить деревце взаимных связей некоторых документов. Мне необходимо получить иерархию дерево причём странным образом. То есть теоретически мне для проведения и/или записи исправленных и/или перерасчитанных документов необходимо получить дерево связей. Причем получить его таким образом что бы я мог начать пробегать это дерево от листьев. Мне необходимо восстанавливать для обработки последовательность, потом рвать связи ведущие от листьев на самого первого родителя по всей цепочке, потом корректировать движения и потом восстанавливать те связи которые до этого были от восстановления ссылки на самого первого родителя в документе который этого родителя исправляет.

    Reply
  90. matytsin_new

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

    Я оцениваю этот метод как «Сложный». Это значит, что далеко не любой программист может сходу разобраться в коде, активно опирающемся на данную методику, что сужает для заказчика выбор людей для сопровождения готового проекта.

    Несмотря на «сложность», часто, применение этого метода бывает единственно возможный выходом. Например, когда у заказчика уже давно используются структуры данных, на которых работали рекурсивные алгоритмы, а теперь данные разрослись и нужно вернуть приемлемое время выполнения.

    Поэтому я считаю, что статья очень хорошая, обсуждать такие приемы на уровне «мне это в практике никогда не требовалось и не потребуется» считаю бесполезным занятием. Умение применять такие алгоритмы позволяет на этапе проектирования решения задачи заказчика оценить практическую осуществимость, сложность задачи по времени реализации и прожорливость реализуемого алгоритма с точки зрения машинного времени и памяти.

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

    Reply
  91. Necytij

    Самое непонятное в статье — почему после бурного обсуждения так и не нашлось места и времени в самой статье дописать условие в запросах на ЭтоГруппа?

    Reply
  92. Дмитрий74Чел

    (116) а еще в п.3 есть СтрЗаменить для Пролог, но нет для Эпилог — а там тоже имя «Номенклатура» прописано.

    Reply
  93. ildarovich

    Прошло давно много времени…

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

    Для этого требуется совсем чуть-чуть изменить предлагаемые запросы.

    Новый варианты — в статье Иерархия без «В ИЕРАРХИИ».

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *