Имя: Пароль:
1C
1С v8
Обращение к реквизиту справочника через точку
, , ,
0 ejikbeznojek
 
05.03.18
15:42
Всем привет.
Есть нетиповая конфа на 8.1
Есть документ с табличной частью.
Перед записью документа есть цикл по этой ТЧ, в которой происходит ряд проверок.

[code]
Для Каждого СтрокаТЧ Из ДокОбъект.Товары Цикл
СтрокаДубля=СтрокиДублей.Получить(СтрокаТЧ.Номенклатура);
Если СтрокаТЧ.Номенклатура.ВидНоменклатуры<>Перечисления.ВидыНоменклаутры.ПодарочныйСертификат Тогда

Замер производительности показывает, что это условие выполняется около 30% времени от всей операции по записи и проведению документа.

ВидНоменклатуры это не составной реквизит с типом значения - перечисление. Индексировать не стоит, но я пробовал поставить это ничего не дало. Если переделать на запрос, тогда будет 7% процентов вместо 30, но я как-то ожидаю увидеть меньше 0,1%.

В чём может быть дело?

[/code]
1 hhhh
 
05.03.18
15:48
(0) запрос в студию
2 ejikbeznojek
 
05.03.18
15:51
https://prnt.sc/in3bi9
Пробовал переделывать
с
Если СтрокаТЧ.Номенклатура.ВидНоменклатуры<>Перечисления.ВидыНоменклаутры.ПодарочныйСертификат Тогда

на
            запрос.текст="ВЫБРАТЬ
            |    Номенклатура.ВидНоменклатуры как ВидНоменклатуры
            |ИЗ
            |    Справочник.Номенклатура КАК Номенклатура
            |ГДЕ
            |    Номенклатура.Ссылка = &Ссылка";
если запрос.выполнить.выгрузить().получить(0).ВидНоменклатуры<>Перечисления.ВидыНоменклаутры.ПодарочныйСертификат Тогда
3 ИС-2
 
naïve
05.03.18
15:53
текст вопроса как у меня - почти 1 в один:

Оптимизация обращений к реквизитам объектов через точку
Оптимизация обращений к реквизитам объектов через точку

написал функцию

Функция ЗначРекв(Источник, ПутьКРеквизитамПовтИсп,ПутьКРеквизитамЖивым = "",ОбновитьПовтИсп = Ложь,ДопПараметры = Неопределено) Экспорт
    ЧерезПовтИсп = Ложь;
    ТипИсточника = ТипЗнч(Источник);
    // объекты нельзя передавать в модуль повторного использования
    Если Документы.ТипВсеСсылки().СодержитТип(ТипИсточника) Тогда
        ЧерезПовтИсп = Истина;
    ИначеЕсли Перечисления.ТипВсеСсылки().СодержитТип(ТипИсточника) Тогда
        ЧерезПовтИсп = Истина;
    ИначеЕсли Справочники.ТипВсеСсылки().СодержитТип(ТипИсточника) Тогда
        ЧерезПовтИсп = Истина;    
    КонецЕсли;
    
    // надо получить все актуальные данные
    Если ПутьКРеквизитамПовтИсп = "" Тогда
        ЧерезПовтИсп = Ложь;    
    КонецЕсли;    
    
    Если ЧерезПовтИсп Тогда
        СтрокаВызоваПовтИсп = "";
        СтрокаВызоваЖивогоВызова = "";
        ИмяЖивогоРеквизита = "";
        
        МассивРеквзитов = _ПовтИсп.РазложитьСтрокуВМассивПодстрок(ПутьКРеквизитамПовтИсп,".");
        МассивРеквзитовЖивых = _ПовтИсп.РазложитьСтрокуВМассивПодстрок(ПутьКРеквизитамЖивым,".");
        
        // все, что после 1-го живого реквизита получаем из актуальных данных
        Если МассивРеквзитовЖивых.Количество() > 0 Тогда
            ИмяЖивогоРеквизита = МассивРеквзитовЖивых[0];
        КонецЕсли;    
        
        // признак, что все последубщие данные надо брать из актуальных данных
        ВЖивойВызов = Ложь;
        Для Каждого ТекРеквизит из МассивРеквзитов Цикл
            Если ТекРеквизит = ИмяЖивогоРеквизита Тогда
                ВЖивойВызов = Истина;
                СтрокаВызоваЖивогоВызова = СтрокаВызоваЖивогоВызова + ?(СтрокаВызоваЖивогоВызова = "","",".") + ТекРеквизит;
            ИначеЕсли не ВЖивойВызов Тогда     
                СтрокаВызоваПовтИсп = СтрокаВызоваПовтИсп + ?(СтрокаВызоваПовтИсп = "","",".") + ТекРеквизит;
            иначе
                СтрокаВызоваЖивогоВызова = СтрокаВызоваЖивогоВызова + ?(СтрокаВызоваЖивогоВызова = "","",".") + ТекРеквизит;;
            КонецЕсли;
        КонецЦикла;    
        
        если не СтрокаВызоваПовтИсп = "" Тогда
            Рез = _ПовтИсп.ЗначРекв(Источник, СтрокаВызоваПовтИсп,ДопПараметры);
        КонецЕсли;
        
        если не ПутьКРеквизитамЖивым = ""
            // передан реквзит после которого не надо вычислять
            и не ВЖивойВызов
            Тогда
            СтрокаВызоваЖивогоВызова = СтрокаВызоваЖивогоВызова + ?(СтрокаВызоваЖивогоВызова = "","",".") + ПутьКРеквизитамЖивым;
        КонецЕсли;    
            
        Если не СтрокаВызоваЖивогоВызова = ""
            //  это составной тип
            и не Рез = Неопределено
            Тогда
            Рез = Вычислить("Рез"+"." + СтрокаВызоваЖивогоВызова);
        КонецЕсли;
    иначе
        СтрокаВызоваЖивогоВызова = ПутьКРеквизитамПовтИсп;
        Если не ПутьКРеквизитамЖивым = ""
            Тогда
            СтрокаВызоваЖивогоВызова =  ?(СтрокаВызоваЖивогоВызова = "","",".") + ПутьКРеквизитамЖивым;
        КонецЕсли;
        Рез = Вычислить("Источник" + "." + СтрокаВызоваЖивогоВызова);
    КонецЕсли;
    
    Возврат Рез;
КонецФункции

в модуле _ПовтИсп:

Функция ЗначРекв(Источник, ПутьКРеквизитам,ДопПараметры) Экспорт
    Реквзиты = РазложитьСтрокуВМассивПодстрок(ПутьКРеквизитам,".");
    Если Реквзиты.количество() = 0 Тогда
        Рез = Источник;
    иначе
        Рез = Вычислить("Источник" + "." +ПутьКРеквизитам);
    КонецЕсли;
    
    Возврат рез;
КонецФункции

Функция РазложитьСтрокуВМассивПодстрок(Знач Стр, Разделитель = ",") Экспорт
    Возврат ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(Стр,Разделитель);
КонецФункции
4 ejikbeznojek
 
05.03.18
15:56
(3) Угу...сейчас попробую, спасибо.
5 ИС-2
 
naïve
05.03.18
16:05
примеры:
            ФормироватьПечатнуюФормуПриЗаписи = _ИзмененияКонфигурации.ЗначРекв(Выборка.Ссылка,"ВидПереписки","ФормироватьПечатнуюФормуПриЗаписи");

    тест = _ИзмененияКонфигурации.ЗначРекв(Ответственный,"ФизЛицо.ГруппаДоступаФизическогоЛица","Код",Ложь,Неопределено);
    тест = _ИзмененияКонфигурации.ЗначРекв(Ответственный,"","ФизЛицо.ГруппаДоступаФизическогоЛица.Код",Ложь,Неопределено);
    тест = _ИзмененияКонфигурации.ЗначРекв(Ответственный,"ФизЛицо","ГруппаДоступаФизическогоЛица.Код",Ложь,Неопределено);
    тест = _ИзмененияКонфигурации.ЗначРекв(Ответственный,"ФизЛицо.ГруппаДоступаФизическогоЛица.Код","",Ложь,Неопределено);
    тест = _ИзмененияКонфигурации.ЗначРекв(Ответственный,"ФизЛицо.ГруппаДоступаФизическогоЛица.Код","ГруппаДоступаФизическогоЛица",Ложь,Неопределено);
6 Cool_Profi
 
05.03.18
16:12
Один раз запросом получи всю тч с нужными реквизитами и ходи по выборке
7 unregistered
 
05.03.18
16:53
(6) Перед записью запросом он получит только то, что было в БД раньше (ДО записи). Осмелюсь предположить, что проверяет он то, что в объекте и ещё не записано.

Но в любом случае для толковой оптимизации надо смотреть весь алгоритм проверок. Потому как в зависимости от того что именно и как проверяется необходимо получать все данные для проверок и перебирать ТОЛЬКО их (а не всю табличную часть).
8 dezm00nd
 
05.03.18
17:01
(7) Надо ТЧ из объекта в ВТ закинуть
9 Badjo
 
05.03.18
17:18
(0) Когда ты обращаешься к реквизиту "ссылки" через точку то возможны два варианта:
1. 1С находит по ссылке объект справочника в кеше и берет данные оттуда - всё работает быстро. (Это к тому что если ты будешь смотреть последовательно несколько реквизитов, то падение скорости у тебя будет только на первом обращении к реквизиту ссылки).
2. 1С не находит объект для ссылки в кеше и считывает объект из базы и помещает его в кеш. Соответственно уходит уйма времени на обращение сервера 1С к серверу SQL.

У тебя второй вариант еще и в цикле.

соответственно тебе нужно избавится от множественных обращений к SQL базе. Ты можешь взять табличную часть документа выгрузить ее таблицу значений (документ же не записан еще). Передать эту ТЗ в запрос и уже в запросе добавить необходимые данные для анализа (либо просто реквизит ВидНоменклатуры, либо через ВЫБОР КОГДА ТОГДА сразу отметить те номенклатуры которые <>Перечисления.ВидыНоменклаутры.ПодарочныйСертификат).
Результат запроса выгружаешь в Таблицу значений и проводишь свои манипуляции с данной ТЗ.
В конце просто обновляешь табличную часть на таблицу значений через ТабличнаяЧасть.Загрузить(ТЗ).

В любом случае всегда старайся оценить приведет ли твой код к обращению к данным SQL или нет. Чем больше раз дернули SQL "по-пустякам" тем "хуже".
10 NorthWind
 
05.03.18
22:52
(9) +100. Обращение через точку к полям ссылки - тормоз, но это же и очевидно... было бы странно если бы было по-другому.
11 Armando
 
05.03.18
23:41
(0) сколько строк в ТЧ обычно?
12 ejikbeznojek
 
05.03.18
23:42
(11) 200-300
13 Armando
 
05.03.18
23:44
(12) сколько при этом уникальных значений по номенклатуре?
14 ejikbeznojek
 
05.03.18
23:51
(5) В общем попробовал.
Если я всё верно понял, то всё сводится к
Рез = Вычислить("Источник" + "." +ПутьКРеквизитам);
Я не совсем понял разницу между живым и повторным вызовом.
Я так понял вся разница только к пути к реквизитам. Прочитав тему по ссылке, я подумал что при живом вызове должен результат кэшироваться, но это видимо просто в кэше при вычислить?

Когда я скопипастил в копию базы, и начал получать значения на 1 и тот же документ не перезаходя, а тупо подряд перезаписываю документ. + я был единственным пользователем в базе, я получил странный % выполнения этой строчки Рез = Вычислить("Источник" + "." +ПутьКРеквизитам);
1й раз - 50%
2й раз - 70%
3й раз - 38%
1й раз 50% от времени вы
15 ejikbeznojek
 
05.03.18
23:51
(13) все уникальные
16 ejikbeznojek
 
05.03.18
23:54
(15) внутри 1го дока имеется ввиду.
17 Armando
 
05.03.18
23:59
(15) есть возможность этот код выполнять в ПриЗаписи?
Просто 200-300 это многовато, чтоб пихать в параметры запроса, а создавать временную таблицу из табличной части дорого.
18 ejikbeznojek
 
06.03.18
00:41
(17) Ну у меня сейчас перед записью код из  (2)  выполняется.
ПриЗаписи мне кажется ничего не изменится.
19 h-sp
 
06.03.18
03:22
(18) ну так делай

запрос.текст="ВЫБРАТЬ РАЗЛИЧЫЕ
            |    Номенклатура.ВидНоменклатуры как ВидНоменклатуры
            |ИЗ
            |    Справочник.Номенклатура КАК Номенклатура
            |ГДЕ
            |    Номенклатура.Ссылка В (&СписокНоменклатуры)";

Запрос.УчтановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"))


у тебя получится не 100 запросов, а один.
20 rphosts
 
06.03.18
04:33
(2) отбирай всё внутри запроса и само сравнение делай тоже в запросе.
21 2mugik
 
06.03.18
05:16
(18)Он тебе намекает что если проверка=ложь редкий случай то можно записать в базу, а потом проверять.
22 Адинэснег
 
06.03.18
08:08
(0) запросы в цикле
делай запрос до цикла
23 lodger
 
06.03.18
09:10
моддим (19)

запрос.текст="ВЫБРАТЬ РАЗЛИЧЫЕ
            |    Номенклатура.Ссылка как Сертификат
            |ИЗ
            |    Справочник.Номенклатура КАК Номенклатура
            |ГДЕ
            |    Номенклатура.Ссылка В (&СписокНоменклатуры)
            |И Номенклатура.ВидНоменклатуры = значение(Перечисление.ВидыНоменклаутры.ПодарочныйСертификат)";

Запрос.УчтановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));

дописываем в (0)

МассивСертификатов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Сертификат");
Для Каждого СтрокаТЧ Из ДокОбъект.Товары Цикл
СтрокаДубля=СтрокиДублей.Получить(СтрокаТЧ.Номенклатура);
Если МассивСертификатов.Найти(СтрокаТЧ.Номенклатура) <> Неопределено Тогда

... хэппиэнд
24 ИС-2
 
naïve
12.03.18
09:14
(14) да, можно получать уже закэшированные значения (из модуля повторного использования) или вычислять вновь. Зависит от вероятности изменения данных. Например, вероятность, что сменится ИНН у контргагента ниже, чем смена контрагента в заказе