Имя: Пароль:
1C
1С v8
Помогите оптимизировать код.
,
0 Temdj
 
17.02.15
14:53
Здравствуйте.
Заранее извиняюсь за оформление поста.

Код заполняет документ из табличной части обработки, которая заполняется из DBF файла.

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

Что с ним можно сделать?

КОД:

Для каждого СтрокаТЧ из ТЧ цикл
        
    Попытка
        НачатьТранзакцию();
        СтрокаДата = Cтрока(Формат(Дата(СтрокаТЧ.DATE),"ДФ=dd.MM.yyyy")) + " " + СтрокаТЧ.TIME;
            ДатаВремя = Дата(СтрокаДата);
            
            Сотрудник = Справочники.СотрудникиОрганизаций.НайтиПоКоду(Лев(СтрокаТЧ.CONDUCTOR,4));
            НомерМарш = СокрЛП(СтрокаТЧ.ROUTE_NUM);
            ПутевойЛист = ПолучитьПутевойЛист(Сотрудник, НомерМарш, ДатаВремя);
            
            
            Запрос = Новый Запрос("ВЫБРАТЬ
            |    уатВыручкаВодителей.Ссылка
            |ИЗ
            |    Документ.уатВыручкаВодителей КАК уатВыручкаВодителей
            |ГДЕ
            |    уатВыручкаВодителей.ПутевойЛист = &ПутевойЛист");
            Запрос.УстановитьПараметр("ПутевойЛист", ПутевойЛист);
            
            Результат = Запрос.Выполнить();
            
            Если Результат.Пустой() тогда
                
                Запрос_2 = Новый Запрос("ВЫБРАТЬ
                |    уатВыручкаВодителей.Ссылка
                |ИЗ
                |    Документ.уатВыручкаВодителей КАК уатВыручкаВодителей
                |ГДЕ
                |    уатВыручкаВодителей.Терминал = &Терминал
                |    И уатВыручкаВодителей.Сотрудник = &Сотрудник
                |    И уатВыручкаВодителей.ПутевойЛист = &Пустая
                |    И уатВыручкаВодителей.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания");
                Запрос_2.УстановитьПараметр("Терминал", СокрЛП(СтрокаТЧ.TRM_ID));
                Запрос_2.УстановитьПараметр("Сотрудник", Сотрудник);
                Запрос_2.УстановитьПараметр("Пустая", Документы.уатПутевойЛист.ПустаяСсылка());
                Запрос_2.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
                Запрос_2.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
                
                Результат_2 = Запрос_2.Выполнить();
                
                Если Результат_2.Пустой() тогда
                    
                    Документ = Документы.уатВыручкаВодителей.СоздатьДокумент();
                    уатОбщегоНазначенияТиповые.ЗаполнитьШапкуДокумента(Документ, глЗначениеПеременной("глТекущийПользователь"));
                    Документ.Дата = КонецДня(ДатаВремя);
                    Документ.Сотрудник = Сотрудник;
                    Документ.Водитель = ПутевойЛист.Водитель1;
                    Документ.Терминал = СтрокаТЧ.TRM_ID;
                    Документ.ПутевойЛист = ПутевойЛист;
                    
                Иначе
                    Выборка_2 = Результат_2.Выбрать();
                    Выборка_2.Следующий();
                    Документ = Выборка_2.Ссылка.ПолучитьОбъект();
                КонецЕсли;
                
            Иначе
                Выборка = Результат.Выбрать();
                Выборка.Следующий();
                Документ = Выборка.Ссылка.ПолучитьОбъект();
                
            КонецЕсли;
            
            НоваяСтрока = Документ.РасшифровкаТранзакций.Добавить();
            НоваяСтрока.Билет = Справочники.уатБилеты.НайтиПоКоду(СтрокаТЧ.PRTYPE);
            НоваяСтрока.Количество = 1;
            НоваяСтрока.Сумма = Число(СокрЛП(СтрокаТЧ.TARIF))/100;
            НоваяСтрока.НомерРейса = СтрокаТЧ.N_REIS;
            
            Если СокрЛП(СтрокаТЧ.TTYPE) = "2" тогда
                НоваяСтрока.Маршрут = ПолучитьМаршрутТроллейбуса(СтрокаТЧ.ROUTE_NUM);
            ИначеЕсли СокрЛП(СтрокаТЧ.TTYPE) = "1" тогда
                НоваяСтрока.Маршрут = ПолучитьМаршрутАвтобуса(ПутевойЛист,СтрокаТЧ.ROUTE_NUM);
            КонецЕсли;
            Документ.РасшифровкаТранзакций.Свернуть("Маршрут,Билет,НомерРейса","Количество,Сумма");
            Документ.Количество = Документ.РасшифровкаТранзакций.Итог("Количество");
            Документ.Сумма        = Документ.РасшифровкаТранзакций.Итог("Сумма");
            
            Документ.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
            
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
        КонецПопытки;    
КонецЦикла;
1 H A D G E H O G s
 
17.02.15
14:55
(0) Вынести запросы за транзакцию, как минимум.
2 H A D G E H O G s
 
17.02.15
14:56
(0) Прекратить писать копрокот.
3 ShoGUN
 
17.02.15
14:58
(0) А нафига в транзакции _читать_?
4 ShoGUN
 
17.02.15
15:00
А за запросы в цикле надо отрывать конечности.
5 kosts
 
17.02.15
15:02
За вот это положен пожизненных эцих с гвоздями

Справочники.СотрудникиОрганизаций.НайтиПоКоду(Лев(СтрокаТЧ.CONDUCTOR,4))
6 ShoGUN
 
17.02.15
15:03
(5) Это наименьшее из зол, это просто кривовато. А вот запросы в цикле и чтение в транзакции - АДЪ.
7 kosts
 
17.02.15
15:05
(6) А ничего, что сотрудники могут иметь один и тот же код...
Пример - периодически принимаемые/увольняемые...
8 ShoGUN
 
17.02.15
15:10
(7) А ничего, что этот код выполняется в "КоличествоСтрок" раз медленней, чем мог бы?
9 kosts
 
17.02.15
15:14
(8) Уж лучше он в 100 раз медленнее работает, чем не правильно.
Оптимизация неправильно работающего алгоритма бессмысленна.
Если, что я не против оптимизации.
10 ShoGUN
 
17.02.15
15:16
(9) Ну начнём с того, что ты не знаешь, правильно код работает, или нет, поскольку мы не знаем, какая это конфигурация. Может коды в справочнике "сотрудники" и не повторяются. А вот скорость у него однозначно никакая, вне зависимости от.
11 Temdj
 
17.02.15
15:17
(4) Полностью согласен, но не могу понять как по другому искать нужные документы.
(5) Там по табельному номеру, это нормально, имхо.
(7) Ну тут либо есть сотрудник, либо его нет - конфа такая... Ну или я её так понял...
(8) Это отвратительно не приятно, но по этому я и обратился сюда...
12 Temdj
 
17.02.15
15:17
(10) Период в день 10 мин примерно..
13 ShoGUN
 
17.02.15
15:21
(11) Опиши словами алгоритм, сначала. Почётче, желательно.
14 kosts
 
17.02.15
15:21
(10) >Документ.уатВыручкаВодителей КАК уатВыручкаВодителей
Это показывает на УАТ Рарус. Смотрим Справочник сотрудники->Контроль уникальности отсутствует.
15 ShoGUN
 
17.02.15
15:23
(14) Завидую вашим телепатическим способностям :) У меня нет УАТ, чтобы посмотреть.
16 Зеленый пень
 
17.02.15
15:24
(0) "Выяснилось, что пропускаются некоторые строчки из ТЧ." - значит надо выяснить, что не так конкретно с этими строчками.
Код в целом "стабильный".
17 kosts
 
17.02.15
15:28
(0) Сделай замер производительности, посмотри, где основной затык.
18 Temdj
 
17.02.15
15:32
код с комментариями.
(16) Причина в конфликтах блокировок.


Функция ЗагрузитьДанные() Экспорт
    
    //Очистим все документы с выручкой терминалов за выбраный период
    ОчиститьДокументы();
    
    //Обработаем кажду строчку, все колонки имеют примитивные типы число, строка, булево.
    Для каждого СтрокаТЧ из ТЧ цикл
        
        Попытка
            //соберем дату приема выручки из колонки даты и  колонки времени
            СтрокаДата = Строка(Формат(Дата(СтрокаТЧ.DATE),"ДФ=dd.MM.yyyy")) + " " + СтрокаТЧ.TIME;
            ДатаВремя = Дата(СтрокаДата);
            
            //Определим Сотрудника, маршрут и найдем путевой лист.
            Сотрудник = Справочники.СотрудникиОрганизаций.НайтиПоКоду(Лев(СтрокаТЧ.CONDUCTOR,4));
            НомерМарш = СокрЛП(СтрокаТЧ.ROUTE_NUM);
            ПутевойЛист = ПолучитьПутевойЛист(Сотрудник, НомерМарш, ДатаВремя);
            
            //соберем все документы выручки по этому путевому листу
            Запрос = Новый Запрос("ВЫБРАТЬ
            |    уатВыручкаВодителей.Ссылка
            |ИЗ
            |    Документ.уатВыручкаВодителей КАК уатВыручкаВодителей
            |ГДЕ
            |    уатВыручкаВодителей.ПутевойЛист = &ПутевойЛист");
            Запрос.УстановитьПараметр("ПутевойЛист", ПутевойЛист);
            
            Результат = Запрос.Выполнить();
            
            //Если нет таких документов проверим не появился ли документ с выручкой без путевого листа
            //вся нам выручка нужна, вся нам выручка важна.
            Если Результат.Пустой() тогда
                
                Запрос_2 = Новый Запрос("ВЫБРАТЬ
                |    уатВыручкаВодителей.Ссылка
                |ИЗ
                |    Документ.уатВыручкаВодителей КАК уатВыручкаВодителей
                |ГДЕ
                |    уатВыручкаВодителей.Терминал = &Терминал
                |    И уатВыручкаВодителей.Сотрудник = &Сотрудник
                |    И уатВыручкаВодителей.ПутевойЛист = &Пустая
                |    И уатВыручкаВодителей.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания");
                Запрос_2.УстановитьПараметр("Терминал", СокрЛП(СтрокаТЧ.TRM_ID));
                Запрос_2.УстановитьПараметр("Сотрудник", Сотрудник);
                Запрос_2.УстановитьПараметр("Пустая", Документы.уатПутевойЛист.ПустаяСсылка());
                Запрос_2.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
                Запрос_2.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
                
                Результат_2 = Запрос_2.Выполнить();
            КонецЕсли;
            //получим/создадим документ выручки
            Если НЕ Результат.Пустой() тогда
                Выборка = Результат.Выбрать();
                Выборка.Следующий();
                
                НачатьТранзакцию();
                Документ = Выборка.Ссылка.ПолучитьОбъект();
            ИначеЕсли Не Результат_2.Пустой() тогда
                Выборка_2 = Результат_2.Выбрать();
                Выборка_2.Следующий();
                
                НачатьТранзакцию();
                Документ = Выборка_2.Ссылка.ПолучитьОбъект();
            Иначе
                НачатьТранзакцию();
                Документ = Документы.уатВыручкаВодителей.СоздатьДокумент();
                уатОбщегоНазначенияТиповые.ЗаполнитьШапкуДокумента(Документ, глЗначениеПеременной("глТекущийПользователь"));
                Документ.Дата = КонецДня(ДатаВремя);
                Документ.Сотрудник = Сотрудник;
                Документ.Водитель = ПутевойЛист.Водитель1;
                Документ.Терминал = СтрокаТЧ.TRM_ID;
                Документ.ПутевойЛист = ПутевойЛист;
            КонецЕсли;
            //Заполним ТЧ документа по строке ТЗ
            НоваяСтрока = Документ.РасшифровкаТранзакций.Добавить();
            НоваяСтрока.Билет = Справочники.уатБилеты.НайтиПоКоду(СтрокаТЧ.PRTYPE);
            НоваяСтрока.Количество = 1;
            НоваяСтрока.Сумма = Число(СокрЛП(СтрокаТЧ.TARIF))/100;
            НоваяСтрока.НомерРейса = СтрокаТЧ.N_REIS;
            
            Если СокрЛП(СтрокаТЧ.TTYPE) = "2" тогда
                НоваяСтрока.Маршрут = ПолучитьМаршрутТроллейбуса(СтрокаТЧ.ROUTE_NUM);
            ИначеЕсли СокрЛП(СтрокаТЧ.TTYPE) = "1" тогда
                НоваяСтрока.Маршрут = ПолучитьМаршрутАвтобуса(ПутевойЛист,СтрокаТЧ.ROUTE_NUM);
            КонецЕсли;
            Документ.РасшифровкаТранзакций.Свернуть("Маршрут,Билет,НомерРейса","Количество,Сумма");
            Документ.Количество = Документ.РасшифровкаТранзакций.Итог("Количество");
            Документ.Сумма        = Документ.РасшифровкаТранзакций.Итог("Сумма");
            
            Документ.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
            //Закроем транзакцию
            ЗафиксироватьТранзакцию();
        Исключение
            //тут надо бы ввести описание ошибки и сообщить об этом.
            ОтменитьТранзакцию();
        КонецПопытки;    
    КонецЦикла;
КонецФункции
19 hhhh
 
17.02.15
15:36
//тут надо бы ввести описание ошибки и сообщить об этом.

дык выведите описание ошибки, покажите.
20 ShoGUN
 
17.02.15
15:43
(18) Во-первых, запросы лучше вынести из попытки, но это мелочи. Чтобы не было ошибок с правами(а вдруг?) - можно использовать ВЫБРАТЬ РАЗРЕШЕННЫЕ.
Во-вторых - либо получи таблицу всех доков выручки за период(И связанных с путевыми листами, и несвязанных), и в ней ищи по своим условиям, либо помести свою ТЧ во временную таблицу и вместо условий работай JOIN-ами.
21 ShoGUN
 
17.02.15
15:45
+(20) Смысл в том, чтобы получить один запрос и один результат запроса, и с ним работать, вместо цикла с запросами и работой с каждым результатом по отдельности.
22 Зеленый пень
 
17.02.15
15:52
(18) Метод борьбы с блокировками один - накладывать свои (в режиме управляемых блокировок, конечно).
23 Temdj
 
17.02.15
16:05
(21) Это я понимаю. Как реализовать не знаю.

"вместо условий работай JOIN-ами" - что значит?
Можно пример?
24 ShoGUN
 
17.02.15
16:09
(23) Боюсь, пример будет не очень понятным. Приведи ещё функцию ПолучитьПутевойЛист, если можно.
25 Temdj
 
17.02.15
16:16
(24) Здесь в DBF может висеть либо водитель или кондуктор, по этому я сначала смотрю по кондуктору, а потом по водителю.

Функция ПолучитьПутевойЛист(Кондуктор, НомерМаршрута, ДатаОплаты)
    
    Запрос = Новый Запрос("ВЫБРАТЬ РАЗЛИЧНЫЕ
    |    уатПутевойЛистЗадание.Ссылка
    |ИЗ
    |    Документ.уатПутевойЛист.Задание КАК уатПутевойЛистЗадание
    |ГДЕ
    |    уатПутевойЛистЗадание.Ссылка.Сотрудник1 = &Кондуктор
    |    И уатПутевойЛистЗадание.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2");
    
    Запрос.УстановитьПараметр("Дата1", НачалоДня(ДатаОплаты));
    Запрос.УстановитьПараметр("Дата2", КонецДня(ДатаОплаты));
    Запрос.УстановитьПараметр("Кондуктор",Кондуктор);
    
    Результат = Запрос.Выполнить();
    
    Если Результат.Пустой() тогда
        
        Запрос = Новый Запрос("ВЫБРАТЬ РАЗЛИЧНЫЕ
        |    уатПутевойЛистЗадание.Ссылка
        |ИЗ
        |    Документ.уатПутевойЛист.Задание КАК уатПутевойЛистЗадание
        |ГДЕ
        |    уатПутевойЛистЗадание.Ссылка.Водитель1 = &Кондуктор
        |    И уатПутевойЛистЗадание.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2");
        
        Запрос.УстановитьПараметр("Дата1", НачалоДня(ДатаОплаты));
        Запрос.УстановитьПараметр("Дата2", КонецДня(ДатаОплаты));
        Запрос.УстановитьПараметр("Кондуктор",Кондуктор);
        
        Результат = Запрос.Выполнить();
        
    КонецЕсли;
    
    Выборка = Результат.Выбрать();
    
    Пока Выборка.Следующий() цикл
        
            Возврат Выборка.Ссылка;
                    
    КонецЦикла;
    
КонецФункции
26 Temdj
 
17.02.15
16:25
+(25) его лучше не смотреть... Ибо стыдно.
27 ShoGUN
 
17.02.15
16:28
(25) Бить тебя надо за такие функции.
Мне тяжко такое без конфы и конструктора писать, если время терпит - вечером могу сесть написать хотя бы пример.
28 Лефмихалыч
 
17.02.15
16:30
повезло троллейбусному депо...
29 kosts
 
17.02.15
16:32
У тебя в нескольких местах проверка на пустую выборку и новый запрос. Возможно будет лучше делать за один запрос.

Запрос из (25) примерно так. Но думаю основной тормоз это из-за записи документов.

ВЫБРАТЬ
    Таблица.Ссылка
ИЗ
    (ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
        1 КАК Порядок,
        уатПутевойЛистЗадание.Ссылка КАК Ссылка
    ИЗ
        Документ.уатПутевойЛист.Задание КАК уатПутевойЛистЗадание
    ГДЕ
        уатПутевойЛистЗадание.Ссылка.Сотрудник1 = &Кондуктор
        И уатПутевойЛистЗадание.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
        2,
        уатПутевойЛистЗадание.Ссылка
    ИЗ
        Документ.уатПутевойЛист.Задание КАК уатПутевойЛистЗадание
    ГДЕ
        уатПутевойЛистЗадание.Ссылка.Водитель1 = &Кондуктор
        И уатПутевойЛистЗадание.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2) КАК Таблица
30 kosts
 
17.02.15
16:33
(29) + Упорядочить по порядку
31 ShoGUN
 
17.02.15
16:36
(29) По хорошему, надо Дату и Кондуктора добавить в поля выборки, т.к. по ним надо джойнить потом.