Если у справочника в свойствах задано Иерархический справочник, то может возникнуть необходимость найти родителя элемента справочника. Количество уровней иерархии в справочнике может быть как ограниченным, так и без ограничений.
Если количество уровней фиксированное, то через точку можно получить родительский элемент любого уровня.
РодительСсылка = СправочникСсылка.Родитель.Родитель.Родитель;
Если количество уровней не ограничено, то можно на встроенном языке 1С написать универсальную функцию, которая определит самую верхнюю группу.
Определить самого верхнего родителя
Универсальную функцию для определения самого верхнего родителя элемента можно реализовать с помощью встроенного языка программирования. Функции на вход подаётся ссылка на элемент иерархического справочника, а возвращается ссылка на самую верхнюю группу.
Функция ПолучитьРодителя(СправочникСсылка)Пока НЕ СправочникСсылка.Родитель.Пустая() Цикл
СправочникСсылка = СправочникСсылка.Родитель;
КонецЦикла;Возврат СправочникСсылка;КонецФункции
Определить самого верхнего родителя с использованием запроса
В запросе можно найти родителей элемента обратившись к свойствам через точку или с помощью левого соединения. Данные методы не универсальны, так как уровень вложенности фиксирован в запросе. Такой метод подходит, когда заранее известно количество уровней иерархического справочника. Функция на вход принимает ссылку на элемент иерархического справочника, а возвращает ссылку на группу N-уровня.
Функция ПолучитьРодителейЗапросе(СправочникСсылка) Запрос
= Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Справочник1.Родитель.Родитель.Родитель КАК Родитель
|ИЗ
| Справочник.Справочник1 КАК Справочник1
|ГДЕ
| Справочник1.Ссылка = &Справочник1Ссылка"; Запрос
.УстановитьПараметр("Справочник1Ссылка", СправочникСсылка);
Результат = Запрос.Выполнить();Если НЕ Результат.Пустой() Тогда Выборка
= Результат.Выбрать();
Выборка.Следующий();
Возврат Выборка.Родитель;КонецЕсли;Возврат Справочники.Справочник1.ПустаяСсылка();КонецФункции
КАК у элемента справочника быстро найти самого верхнего родителя? |
Я |
04.02.09 — 15:14
вопрос, собственно в теме
1 — 04.02.09 — 15:15
Пока ЗначениеЗаполнено(ВыбСправочник.Родитель) Цикл
ВыбСправочник = ВыбСправочник.Родитель;
КонецЦикла
2 — 04.02.09 — 15:17
Спасибо, попробую.
3 — 04.02.09 — 15:21
а в запросе?
4 — 04.02.09 — 15:22
2… варианта… есть тебе какой попроще и подурацкей, или посложнее и покрасивей?
5 — 04.02.09 — 15:25
правильный.
6 — 04.02.09 — 15:26
(4) Желательно для неограниченного по иерархии справочника
7 — 04.02.09 — 15:35
Для семерки:
Set NoCount On
Declare @элТовар Char(9)
Set @элТовар = ‘ 2IW ‘
While 1 = 1 Begin
Select
@элТовар = ParentID
From
спрНоменклатура
Where
ID = @элТовар And ParentID != ‘ 0 ‘
IF @@RowCount = 0 Break
End
Select @элТовар
Для восьмерки — по аналогии.
8 — 04.02.09 — 15:36
(7) Нет в восьмерке в запросах переменных.
9 — 04.02.09 — 15:37
(8) Сочуствую, конечно, восьмерке… Но, то же самое ADO еще никто не отменял
10 — 04.02.09 — 15:38
для реально неограниченного количества уровней в запросе не получить. но если точно нать, что реальное количество уровней иерархии например не больше пяти — тогда запросто.
11 — 04.02.09 — 15:39
В запросе быстрее. Имхо
12 — 04.02.09 — 15:39
(10) Как это не получить? См. (7).
13 — 04.02.09 — 15:40
(12) я про запросы 1С 8.х. Там к сожалению ни циклов ни рекурсии нет.
14 — 04.02.09 — 15:40
(6) А если в Иерархии с условием Родитель=Справочник.ПустоеЗначение ?
15 — 04.02.09 — 15:40
(13) Тогда см (9)
16 — 04.02.09 — 15:42
(15) ну тогда скинь мне АДО драйвер для файловой восьмерки умнег
17 — 04.02.09 — 15:42
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка = &Ссылка
ИТОГИ ПО
Ссылка ТОЛЬКО ИЕРАРХИЯ
А такой способ не устраивает?
18 — 04.02.09 — 15:42
(5) правильного не знаю.
(6) если для ограниченного, то можно так
ВЫБОР
КОГДА РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации.Родитель = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
ТОГДА РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации.Родитель
ИНАЧЕ ВЫБОР
КОГДА РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
ТОГДА РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации.Родитель.Родитель
ИНАЧЕ РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации.Родитель.Родитель.Родитель
КОНЕЦ
КОНЕЦ
19 — 04.02.09 — 15:42
(16) А нефиг пользоваться левыми продуктами. Восьмерка, файловая. На до же…
20 — 04.02.09 — 15:43
Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка
|ИТОГИ ПО
| Ссылка ТОЛЬКО ИЕРАРХИЯ»;
Запрос.УстановитьПараметр(«Ссылка»,МояСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
ВерхнийУровень = Выборка.Ссылка;
21 — 04.02.09 — 15:43
(18) Опупеть… «Здраствуйте, тормоза!!!»
22 — 04.02.09 — 15:43
(20) Баян
23 — 04.02.09 — 15:43
(17) угу, что-то вроде. потом можно попробовать прикрутить по левому соединению другую таблицу. Но у меня в правилах не получилось указать В Иерархии
24 — 04.02.09 — 15:44
(21) какие тормоза))) нет никаких тормозов. Я как вариант привел, который будет работать, предложи лучше
25 — 04.02.09 — 15:45
(20) не годится, потому что результат нельзя использовать например в подзапросе.
26 — 04.02.09 — 15:46
насчет семерки: как-то Рупор приводил такой код :
ВерхнийРодитель=ЗначениеИзСтрокиВнутр(Шаблон(«[ЗначениеВСтрокуВнутр(Спр»+СтрЗаменить(Формат(«»,»С»+(Спр.Уровень()-1)),» «,».Родитель»)+»)]»));
27 — 04.02.09 — 15:46
(25) Можно, выгружайте в Таблицу, дальше Временные таблицы
28 — 04.02.09 — 15:46
(19) ОК. В монопольном режиме 1С 8.х твое АДО будет работать?
29 — 04.02.09 — 15:46
+26 вот где красота и изящество
30 — 04.02.09 — 15:47
(0) Логичнее использовать не Иерархию справочника, а например Номенклатурные группы
31 — 04.02.09 — 15:49
(26) Это да.. Тот еще извращенец «Извращенец» в данном случае — в хорошем смысле.
(28) Мое ODBC — будет. А ваше восьмерошное ADO — нет, конечно
32 — 04.02.09 — 15:49
(27) тогда это ничем не отличается от получения главного родителя в 1С-ком коде.
33 — 04.02.09 — 15:50
(24) А засветика что скуль при таком запросе выполнит?
34 — 04.02.09 — 15:51
А вообще у товара есть полный код, который косухами разделен. Типа
ИскомыйРодитель = спр.НайтиПоКоду(Лев(товар.полныйкод,1,Найти(товар.полныйкод,»/»)-1))
35 — 04.02.09 — 15:51
Да не лучше, но запрос быстрее + методично правильнее.
36 — 04.02.09 — 15:52
(34) Ай молодца! Меня практически каждый твой пост радует! Зайди-ка на закладочку «Нумерация» в свойствах справочника.
37 — 04.02.09 — 15:54
(36) По коду будет искатся верхний уровень, а нам больше ничего и не надо ))) Меня твои посты тоже радуют глупостью…
38 — 04.02.09 — 15:55
(37) У тебя СП украли? Почитай
«<Родитель> (необязательный)
Тип: СправочникСсылка. Родитель, в пределах которого нужно выполнять поиск. Если не указан, то поиск будет проводиться во всем справочнике. «
39 — 04.02.09 — 15:56
(38) НайтиПоКоду(<Код>, <Поиск по полному коду>, <Родитель>, <Владелец>)
В чем проблема?
40 — 04.02.09 — 15:57
(37) Юморной однако.
Хотя в 7.7 есть возможность получить «полный код» справочника через «»
41 — 04.02.09 — 15:57
(39) Еще раз внимательно (38) прочитай. А потом все-таки загляни на закладочку «Нумерация».
42 — 04.02.09 — 15:57
(40) В 8.х тоже
43 — 04.02.09 — 15:58
(39) Ты предлогаешь в элементе справочника хранить поле с програмно записанным путем к родителю, имхо это не правильно
44 — 04.02.09 — 15:58
СправочникСсылка.<Имя справочника> (CatalogRef.<Имя справочника>)
ПолныйКод (FullCode)
Синтаксис:
ПолныйКод()
Возвращаемое значение:
Тип: Строка. Строка, включающая код элемента и коды всех вышестоящих элементов.
Коды выводятся слева направо, начиная с самого верхнего уровня, разделяются символом «/».
Описание:
Получает код элемента справочника с учетом кодов всех вышестоящих элементов.
Пример:
Сообщить(«Полный код данного элемента: » +
СсылкаНаЭлемент.ПолныйКод());
45 — 04.02.09 — 15:58
(42) Нельзя, только програмно. посмотри свой код и заметишь «товар.полныйкод»
46 — 04.02.09 — 15:58
как всегда на мисте чем тупее вопрос тем живее обсуждение.
47 — 04.02.09 — 15:59
(46) а ты предложи красивое решение
48 — 04.02.09 — 16:00
(44) о_0 Гуру, не замечал такого !!
(47) получается (44) самое красивое
49 — 04.02.09 — 16:00
а я предлагал в (10) при наличии ряда условий, а без них штатно запросом низя.
50 — 04.02.09 — 16:01
(49) что ты так упрям, запросом можно!!!
51 — 04.02.09 — 16:02
(50) твое решение в (17) — это не запрос, а несколько запросов — тогда лучше делать кодом без затей.
52 — 04.02.09 — 16:03
ПолныйКод — это метод.
ИскомыйРодитель = спр.НайтиПоКоду(Лев(товар.полныйкод(),1,Найти(товар.полныйкод(),»/»)-1))
и что тут неправильного?
53 — 04.02.09 — 16:04
КАК у элемента справочника быстро найти самого верхнего родителя?
По этому сабжу запросом можно, ты же усложняешь задачу, но и более сложную задачу тоже лучше делать через запрос.
54 — 04.02.09 — 16:05
(50) блин! как?
55 — 04.02.09 — 16:05
(53) да не в запросе это получается, а уже в выборке.
56 — 04.02.09 — 16:06
(52) Неправильно то, что, если не стоит галка Контроль уникальности и серии кодов не в пределах всего справочника, то неизвестно, что ты найдешь.
57 — 04.02.09 — 16:09
(56)если уникальности нет, то да, не катит.
а если не в пределах спр, то так, по полному коду.
ИскомыйРодитель = спр.НайтиПоКоду(Лев(товар.полныйкод(),1,Найти(товар.полныйкод(),»/»)-1), Истина)
58 — 04.02.09 — 16:10
(57) Вот так уже чуток лучше
59 — 04.02.09 — 16:12
Наиболее практичное решение на данный момент (17)… увы.
Более «красивое» можно добится в СКД используя связи наборов данных с передачей параметра. Но по факту это лишь имитация циклических запросов с изменением параметра.
60 — 04.02.09 — 16:13
(59) + Сорь … имел ввиду (18)
61 — 04.02.09 — 16:13
(59) НайтиПоКоду должно быстрее отработать, при перечисленных выше ограничениях.
62 — 04.02.09 — 16:14
(59) а как мне узнать в (17) у конкретного элемента родителя верхнего уровня?
63 — 04.02.09 — 16:16
(62) так он там один и будет в выборке
64 — 04.02.09 — 16:17
автор-то, автор почему молчит.
65 — 04.02.09 — 16:19
(62) Опечатался. См. (60)
66 — 04.02.09 — 16:22
(63) В выборке, а не в запросе..
1С-ина дополнит (17) запрос еще своими запросами..
67 — 04.02.09 — 16:23
(65) я уже понял
68 — 04.02.09 — 16:24
(65) вообще-то мне просто надо было найти у элемента, мне известного:
например МояНоменклатура — (это ссылка) самую его верхнюю папочку, например его родитель Папка1 а у папка1 родитель папка2 и т.д. вот мне надо самую верхнюю папку и ответ мне был дан сразу, спасибо gr13…. Правда я надеялась, что можно узнать самого верхнего типа так:
ИскомыйРодитель = МояНоменклатура.СамыйВерхнийРодитель;
но в 1С этого не предусмотрели.
69 — 04.02.09 — 16:25
я при решении похожей задачи (правда не со справочниками а с вложенными БП) создал реквизит «ВерхнийУровень»
70 — 04.02.09 — 16:28
(0)самый быстрый — посчитать для всех групп группу верхнего уровня и похранить на время надобности в каком-нить соответствии. и по родителю элемента всегда получим нужный верхний элемент
71 — 04.02.09 — 16:31
(69) а можно подробнее? какая была задача и как воплотили?
72 — 04.02.09 — 16:31
(69) А если папочку, в которой лежит элемент перенести в другого родителя верхнего уровня?
73 — 04.02.09 — 16:34
(72) перезаписывать все элементы на самом деле зависит от задачи
у меня для БП изменение вержнего родиделя невозможно по определению
74 — 04.02.09 — 16:35
(71) а тебе вообще ЗАЧЕМ это надо делать? какая цель? может решение проще есть?
75 — 04.02.09 — 16:50
+1
76 — 04.02.09 — 16:59
(69) Тогда лучше что то вроде «Номенклатурных групп», доп аналитики
vde69
77 — 04.02.09 — 17:02
(76) мы не знаем ЗАЧЕМ, вариантов много
мне это надо было для RLS использовать, там в верхнем БП была группа доступа
Найти верхнего родителя
Автор TataRus, 16 ноя 2021, 11:18
0 Пользователей и 1 гость просматривают эту тему.
Добрый день. Подскажите пожалуйста как найти верхнего Родителя для каждого реквизита из ТЧ и собрать их в СЗ? Значения реквизита я собрала в список, проверила на дубли и дальше пока в тупике.
TataRus, для удобства ответа, пожалуйста вставляйте свой код в виде текста (текста кода, вот тут вверху иконочка есть Код)
а не в виде картинки — как ее править? что бы вам ответ прислать?
если помогло нажмите: Спасибо!
TataRus, Например, найти уровень реквизита и в цикле найти родителя первого уровня для него.
LexaK,
СписокЦфо = Новый СписокЗначений;
Для каждого ТекСтр из Объект.ТабЧасть Цикл
БитЦфо = ТекСтр.ДоговорыДокумента.бит_ЦФО;
Если ЗначениеЗаполнено(БитЦфо) и СписокЦфо.НайтиПоЗначению(БитЦфо) = Неопределено Тогда
СписокЦфо.Добавить(БитЦфо);
КонецЕсли;
КонецЦикла;
Для а = 0 по СписокЦфо.Количество()-1 Цикл
Вроде так
alexandr_ll, я читала про это, но при попытке реализации цикл не срабатывал
TataRus, примерно такой код попробуйте
...
СписокЦфо = Новый СписокЗначений;
СписокРодителей = Новый СписокЗначений;
Для каждого ТекСтр из Объект.ТабЧасть Цикл
БитЦфо = ТекСтр.ДоговорыДокумента.бит_ЦФО;
Если ЗначениеЗаполнено(БитЦфо) и СписокЦфо.НайтиПоЗначению(БитЦфо) = Неопределено Тогда
СписокЦфо.Добавить(БитЦфо);
КонецЕсли;
КонецЦикла;
Для каждого лкСтр Из СписокЦфо Цикл
лкРодительЦФО = ПолучитьРодителя(лкСтр.Значение);
Если СписокРодителей.НайтиПоЗначению(лкРодительЦФО) = Неопределено Тогда
СписокРодителей.Добавить(лкРодительЦФО);
КонецЕсли;
КонецЦикла;
...
КонецЧегоТамУвас;
&НаСервереБезКонтекста
Функция ПолучитьРодителя(лкЦФО)
лкРодитель = лкЦФО;//сама себе родитель, если других нет
Пока ЗначениеЗаполнено(лкРодитель.Родитель) Цикл
лкРодитель = лкРодитель.Родитель;
КонецЦикла;
Возврат лкРодитель;
КонецФункции
- 1 пользователь сказал спасибо!
если помогло нажмите: Спасибо!
LexaK, я так не пробовала, вроде работает, можно дальше идти)
Спасибо большое!
Andrey102019 5 / 4 / 1 Регистрация: 21.10.2019 Сообщений: 194 |
||||||||
1 |
||||||||
Как найти корневую группу элемента справочника?16.01.2020, 12:15. Показов 16312. Ответов 19 Метки нет (Все метки)
Каким образом зная или получив элемент справочника Номенклатура получить самого верхнего/корневого родителя/папки этого элемента номенклатуры? Я как то при доработке делал нечто подобное через процедуру, которая перебирает все дерево справочника и идет до самого верха, но наверно это не очень производительно, да и не помню уже где брал пример этого кода. Может быть есть что-то стандартное, или это можно сделать запросом? Добавлено через 16 минут
Добавлено через 15 секунд
0 |
1870 / 1277 / 458 Регистрация: 16.01.2015 Сообщений: 5,583 |
|
16.01.2020, 12:16 |
2 |
Andrey102019, Один из способов: Добавлено через 20 секунд
0 |
1183 / 723 / 210 Регистрация: 22.04.2013 Сообщений: 5,325 Записей в блоге: 1 |
|
16.01.2020, 12:18 |
3 |
Получение всех родителей элемента В языке запросов не предусмотрено специальных средств для получения всех родителей элемента. Для выполнения задачи можно воспользоваться иерархическими итогами, однако получение иерархических итогов оптимизировано для построения итогов большого количества записей, и не вполне эффективно для получения родителей одного элемента. Для более эффективного получения всех родительских записей элемента, рекомендуется перебирать в цикле его родителей небольшими порциями. взято отсюда
0 |
Andrey102019 5 / 4 / 1 Регистрация: 21.10.2019 Сообщений: 194 |
||||
16.01.2020, 14:13 [ТС] |
4 |
|||
Получение всех родителей элемента спасибо за труд, но это и так понятно, по логике. Вот написал работающий код, остановлюсь на этом
бесконечный цикл не стал брать, чтобы не подвесить систему, 10 уровней думаю будет достаточно для любого справочника
0 |
1183 / 723 / 210 Регистрация: 22.04.2013 Сообщений: 5,325 Записей в блоге: 1 |
|
16.01.2020, 15:42 |
5 |
в том-то и дело, что по логике было удивительно увидеть на ИТС-е слова родитель.родитель.родитель.родитель.родитель ))))
0 |
5 / 4 / 1 Регистрация: 21.10.2019 Сообщений: 194 |
|
16.01.2020, 16:21 [ТС] |
6 |
увидеть на ИТС-е слова родитель.родитель.родитель.родитель.родитель да, я тоже усмехнулся когда такое увидел на ИТСе, хотя некий смысл есть, код короче, но некрасиво. Думаю это следует понимать так — руки у разработчика не дошли, что бы написать такую функцию (хотя не долго бы и написать, в редких случаях функция была бы полезна), взяли и извернулись, пусть коряво, но что-то есть)
0 |
1870 / 1277 / 458 Регистрация: 16.01.2015 Сообщений: 5,583 |
|
16.01.2020, 16:29 |
7 |
Andrey102019, А чем не устраивает ПолныйКод()? Самого кода пять-шесть строчек, быстро и точно. Но… хозяин-барин, плодите родитель.родитель.родитель…
1 |
1183 / 723 / 210 Регистрация: 22.04.2013 Сообщений: 5,325 Записей в блоге: 1 |
|
16.01.2020, 16:32 |
8 |
polax, а при чём тут Андрей? это ты у итс-ников спрашивай )))
0 |
Xomych 856 / 655 / 111 Регистрация: 01.11.2012 Сообщений: 2,410 |
||||
16.01.2020, 16:40 |
9 |
|||
По опыту самое оптимальное это вот
1 |
polax 1870 / 1277 / 458 Регистрация: 16.01.2015 Сообщений: 5,583 |
||||||||
16.01.2020, 16:52 |
10 |
|||||||
Yulunga, ИТС-ники — это их проблемы, я же подсказал может и нетривиальный, но рабочий способ.
Получаем строку примерно вида «002/005/009/045», где 045 код самого элемента по ссылке, остальное — коды родителей. Первый в строке «002» и есть код верхнего родителя.
Добавлено через 11 минут
0 |
1870 / 1277 / 458 Регистрация: 16.01.2015 Сообщений: 5,583 |
|
16.01.2020, 17:02 |
11 |
Сделал пример. Создал Номенклатуру ЧтоТам с уровнем вложенности 10. Код элементу 0035, код верхнего каталога 006 (скрин1) Повесил на кнопку формы Расходной код (скрин2) Результат в Нужное — элементарно превращается в нужное значение. И никакаких запросов и кучи родителей Миниатюры
0 |
Xomych 856 / 655 / 111 Регистрация: 01.11.2012 Сообщений: 2,410 |
||||
17.01.2020, 09:05 |
12 |
|||
Xomych, А если уровень вложенности, 7, 8, 10? Находишь сначала максимальный уровень вложенности запросом, потом генеришь программно запрос Добавлено через 15 минут
1 |
Andrey102019 5 / 4 / 1 Регистрация: 21.10.2019 Сообщений: 194 |
||||||||||||
17.01.2020, 12:03 [ТС] |
13 |
|||||||||||
А чем не устраивает ПолныйКод() устраивает) я просто не знал, что есть такая функция Добавлено через 4 минуты
По опыту самое оптимальное это вот да я в итоге так и сделал, все же ИТСники правы, так короче, хотя могли бы и функцию написать, чем огород городить.
Добавлено через 6 минут
ПолныйКод() А есть еще малознакомые функции типа этой? Которые были бы полезны в некоторых случаях, но их мало кто знает? (или тут чисто индивидуально или просто пройтись по СП и пооткывать все непонятное/незнакомое) Добавлено через 18 минут
это конечно частный случай, но это мой частный случай) Добавлено через 7 минут
0 |
blade_snl 4 / 4 / 1 Регистрация: 02.02.2017 Сообщений: 180 |
||||||||
17.06.2021, 15:09 |
14 |
|||||||
По опыту самое оптимальное это вот
отличная штука! благодарен. имею ввиду такой запрос:
а в итоге получить таблицу с полями: Код | Наименование | Марка ….
0 |
Yulunga 1183 / 723 / 210 Регистрация: 22.04.2013 Сообщений: 5,325 Записей в блоге: 1 |
||||
17.06.2021, 16:37 |
15 |
|||
1 |
blade_snl 4 / 4 / 1 Регистрация: 02.02.2017 Сообщений: 180 |
||||
17.06.2021, 17:14 |
16 |
|||
))) действительно, так стало нагляднее, сразу разобрался, спасибо огромное!
0 |
264 / 219 / 95 Регистрация: 07.04.2011 Сообщений: 1,358 |
|
18.06.2021, 16:17 |
17 |
Текст Запроса…. Вернёт самый верхний каталог
1 |
4 / 4 / 1 Регистрация: 02.02.2017 Сообщений: 180 |
|
18.06.2021, 16:29 |
18 |
Текст Запроса…. Вернёт самый верхний каталог но через ГДЕ — оно же просеет записи…
0 |
Xomych 856 / 655 / 111 Регистрация: 01.11.2012 Сообщений: 2,410 |
||||
19.06.2021, 08:19 |
19 |
|||
blade_snl,
2 |
blade_snl 4 / 4 / 1 Регистрация: 02.02.2017 Сообщений: 180 |
||||||||
19.06.2021, 11:03 |
20 |
|||||||
blade_snl,
ну да, я же написал выше, что разобрался уже, спасибо…
0 |
В продолжение публикации «Транзитивное замыкание запросом» [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 записей регистра сведений «Аналоги номенклатуры» для этих связей.
После транзитивного замыкания аналогии будет сформирована полная таблица аналогов
Из-за фундаментального характера затрагиваемых понятий приведенные примеры, скорее всего, не исчерпывают всего списка применений предложенного приема. Надеюсь, приведенные решения являются достаточно поучительными и послужат хорошей основой для решения и других подобных практических задач.
Еще двум интереснейшим задачам-примерам будет посвящена отдельная статья.