Примерно, в 2019 году я отлаживал некоторую разработку в УТ 10.3.15.9, в ходе которой проводил документ «Реализация товаров и услуг» (РТУ). После многочисленных проведений и я обнаружил, что иногда при проведении происходит длительное зависание (минута-две), когда обычно РТУ проводится в течение 10 секунд. В конфигураторе замер производительности показал топовую строку кода:
УправлениеЗапасамиПартионныйУчет.ДвижениеПартийТоваров(Ссылка, Движения.СписанныеТовары.Выгрузить(),,,,,,,,,,Отказ);
Другие детали конфигуратор скрыл, на тот момент у меня отсутствовал доступ к технологическому журналу (ТЖ), к СУБД тоже отсутствовал (хотя было известно, что это MS SQL Server 2008 R2), и задача была другая, поэтому дальнейшее расследование остановлено.
Прошло время (2018, 2019, 2020), и вот в 2021 году поступила задача: в системе УПП 1.3.162 медленно проводится реализация.
Для начала проверен программный код, и обнаружена следующая конструкция:
Что мы наблюдаем: в модуле РТУ создается COM-соединение с базой MySQL (которая может оказаться достаточно далеко, и связь с которой может оказаться достаточно медленной и с прерываниями), выполняется команда в базе MySQL(которая тоже может быть длительной), и все это выполняется два раза: в процедурах ПередЗаписью, ОбработкаПроведения.
Очевидно, что данный функционал следует перестроить архитектурно, что в первую очередь и сделано:
1) создан регистр сведений «Документы интеграции», в который записывается информация при проведении РТУ;
2) создано регламентное задание, которое соединяется с базой MySQL и выполняет необходимые команды.
Данное изменение перестало мешать и оказалось полезным для разгрузки времени проведения, но не было основной причиной.
На этот раз доступ к ТЖ был, собраны события: CALL, SCALL, DBMSSQL, EXCP.
ТЖ показал:
57:46.476012-60913930,CALL,3,process=rphost,p:processName=test,OSThread=5452,t:clientID=24301,t:applicationName=1CV8,t:computerName=pc1,t:connectID=15597,SessionID=28,Usr=Татьяна1,callWait=0,Interface=fd7b6cc2-dc8e-11d2-b8d0-008048da0335,Iname=IContext_,Method=16,CallID=104925,Mname=call,Memory=6058666,MemoryPeak=11866141,InBytes=0,OutBytes=1048572,CpuTime=468750
57:46.476015-0,Context,3,process=rphost,p:processName=test,OSThread=5452,t:clientID=24301,t:applicationName=1CV8,t:computerName=pc1,t:connectID=15597,SessionID=28,Usr=Татьяна1,Context='
Документ.РеализацияТоваровУслуг.МодульОбъекта : 5406 : ДвиженияПоРегистрам(РежимПроведения, СтруктураШапкиДокумента, ТаблицаПоТоварам, ТаблицаПоСкидкам, ТаблицаПоТаре,
Документ.РеализацияТоваровУслуг.МодульОбъекта : 3595 : УправлениеЗапасамиПартионныйУчет.ДвижениеПартийТоваров(Ссылка, Движения.СписанныеТовары.Выгрузить());'
Длительность=53.1 sec; INSERT INTO #tt72 WITH(TABLOCK) (_Q_000_F_000RRef, _Q_000_F_001_TYPE, _Q_000_F_001_RTRef, _Q_000_F_001_RRRef, _Q_000_F_002RRef, _Q_000_F_003RRef, _Q_000_F_004RRef, _Q_000_F_005RRef, _Q_000_F_006RRef, _Q_000_F_007, _Q_000_F_008, _Q_000_F_009RRef) SELECT<line>T1.Fld17067RRef,<line>T1.Fld17071_TYPE,<line>T1.Fld17071_RTRef,<line>T1.Fld17071_RRRef,<line>T1.Fld17068RRef,<line>T1.Fld17069RRef,<line>T1.Fld17070RRef,<line>T1.Fld17074RRef,<line>T1.Fld17073RRef,<line>T1.Fld17075Balance_,<line>T1.Fld17076Balance_,<line>T1.Fld17072RRef<line>FROM (SELECT<line>T2.Fld17067RRef AS Fld17067RRef,<line>T2.Fld17071_TYPE AS Fld17071_TYPE,<line>T2.Fld17071_RTRef AS Fld17071_RTRef,<line>T2.Fld17071_RRRef AS Fld17071_RRRef,<line>T2.Fld17068RRef AS Fld17068RRef,<line>T2.Fld17069RRef AS Fld17069RRef,<line>T2.Fld17070RRef AS Fld17070RRef,<line>T2.Fld17074RRef AS Fld17074RRef,<line>T2.Fld17073RRef AS Fld17073RRef,<line>T2.Fld17072RRef AS Fld17072RRef,<line>CAST(SUM(T2.Fld17076Balance_) AS NUMERIC(33, 2)) AS Fld17076Balance_,<line>CAST(SUM(T2.Fld17075Balance_) AS NUMERIC(33, 3)) AS Fld17075Balance_<line>FROM (SELECT<line>T3._Fld17067RRef AS Fld17067RRef,<line>T3._Fld17071_TYPE AS Fld17071_TYPE,<line>T3._Fld17071_RTRef AS Fld17071_RTRef,<line>T3._Fld17071_RRRef AS Fld17071_RRRef,<line>T3._Fld17068RRef AS Fld17068RRef,<line>T3._Fld17069RRef AS Fld17069RRef,<line>T3._Fld17070RRef AS Fld17070RRef,<line>T3._Fld17074RRef AS Fld17074RRef,<line>T3._Fld17073RRef AS Fld17073RRef,<line>T3._Fld17072RRef AS Fld17072RRef,<line>CAST(SUM(T3._Fld17076) AS NUMERIC(27, 2)) AS Fld17076Balance_,<line>CAST(SUM(T3._Fld17075) AS NUMERIC(27, 3)) AS Fld17075Balance_<line>FROM dbo._AccumRgT17086 T3<line>WHERE T3._Period = ? AND (T3._Fld22965RRef = ? AND EXISTS(SELECT<line>1<line>FROM dbo._InfoRg14911 T4<line>WHERE (T4._RecorderTRef = 0x000001AE AND T4._RecorderRRef = ?) AND ((T3._Fld17067RRef = T4._Fld14963RRef) AND (T3._Fld17069RRef = T4._Fld15013RRef))) AND (T3._Fld17068RRef IN<line>(SELECT<line>T5._Fld14989RRef AS Q_001_F_000RRef<line>FROM dbo._InfoRg14911 T5<line>WHERE T5._RecorderTRef = 0x000001AE AND T5._RecorderRRef = ?) OR T3._Fld17068RRef = ?)) AND (T3._Fld17076 <> ? OR T3._Fld17075 <> ?) AND (T3._Fld17076 <> ? OR T3._Fld17075 <> ?)<line>GROUP BY T3._Fld17067RRef,<line>T3._Fld17071_TYPE,<line>T3._Fld17071_RTRef,<line>T3._Fld17071_RRRef,<line>T3._Fld17068RRef,<line>T3._Fld17069RRef,<line>T3._Fld17070RRef,<line>T3._Fld17074RRef,<line>T3._Fld17073RRef,<line>T3._Fld17072RRef<line>HAVING (CAST(SUM(T3._Fld17076) AS NUMERIC(27, 2))) <> 0.0 OR (CAST(SUM(T3._Fld17075) AS NUMERIC(27, 3))) <> 0.0<line>UNION ALL SELECT<line>T6._Fld17067RRef AS Fld17067RRef,<line>T6._Fld17071_TYPE AS Fld17071_TYPE,<line>T6._Fld17071_RTRef AS Fld17071_RTRef,<line>T6._Fld17071_RRRef AS Fld17071_RRRef,<line>T6._Fld17068RRef AS Fld17068RRef,<line>T6._Fld17069RRef AS Fld17069RRef,<line>T6._Fld17070RRef AS Fld17070RRef,<line>T6._Fld17074RRef AS Fld17074RRef,<line>T6._Fld17073RRef AS Fld17073RRef,<line>T6._Fld17072RRef AS Fld17072RRef,<line>CAST(CAST(SUM(CASE WHEN T6._RecordKind = 0.0 THEN -T6._Fld17076 ELSE T6._Fld17076 END) AS NUMERIC(21, 2)) AS NUMERIC(27, 2)) AS Fld17076Balance_,<line>CAST(CAST(SUM(CASE WHEN T6._RecordKind = 0.0 THEN -T6._Fld17075 ELSE T6._Fld17075 END) AS NUMERIC(21, 3)) AS NUMERIC(27, 3)) AS Fld17075Balance_<line>FROM dbo._AccumRg17066 T6<line>WHERE (T6._Period > ? OR T6._Period = ? AND (T6._RecorderTRef > 0x000001AE OR T6._RecorderTRef = 0x000001AE AND T6._RecorderRRef >= ?)) AND T6._Period < ? AND T6._Active = 0x01 AND (T6._Fld22965RRef = ? AND EXISTS(SELECT<line>1<line>FROM dbo._InfoRg14911 T7<line>WHERE (T7._RecorderTRef = 0x000001AE AND T7._RecorderRRef = ?) AND ((T6._Fld17067RRef = T7._Fld14963RRef) AND (T6._Fld17069RRef = T7._Fld15013RRef))) AND (T6._Fld17068RRef IN<line>(SELECT<line>T8._Fld14989RRef AS Q_001_F_000RRef<line>FROM dbo._InfoRg14911 T8<line>WHERE T8._RecorderTRef = 0x000001AE AND T8._RecorderRRef = ?) OR T6._Fld17068RRef = ?))<line>GROUP BY T6._Fld17067RRef,<line>T6._Fld17071_TYPE,<line>T6._Fld17071_RTRef,<line>T6._Fld17071_RRRef,<line>T6._Fld17068RRef,<line>T6._Fld17069RRef,<line>T6._Fld17070RRef,<line>T6._Fld17074RRef,<line>T6._Fld17073RRef,<line>T6._Fld17072RRef<line>HAVING (CAST(CAST(SUM(CASE WHEN T6._RecordKind = 0.0 THEN -T6._Fld17076 ELSE T6._Fld17076 END) AS NUMERIC(21, 2)) AS NUMERIC(27, 2))) <> 0.0 OR (CAST(CAST(SUM(CASE WHEN T6._RecordKind = 0.0 THEN -T6._Fld17075 ELSE T6._Fld17075 END) AS NUMERIC(21, 3)) AS NUMERIC(27, 3))) <> 0.0) T2<line>GROUP BY T2.Fld17067RRef,<line>T2.Fld17071_TYPE,<line>T2.Fld17071_RTRef,<line>T2.Fld17071_RRRef,<line>T2.Fld17068RRef,<line>T2.Fld17069RRef,<line>T2.Fld17070RRef,<line>T2.Fld17074RRef,<line>T2.Fld17073RRef,<line>T2.Fld17072RRef<line>HAVING (CAST(SUM(T2.Fld17076Balance_) AS NUMERIC(33, 2))) <> 0.0 OR (CAST(SUM(T2.Fld17075Balance_) AS NUMERIC(33, 3))) <> 0.0) T1<line>p_0: 40210701000000<line>p_1: 0x00000000000000000000000000000000<line>p_2: 0x90DBA4BF015B9A3411EBC2B9D1775CFC<line>p_3: 0x90DBA4BF015B9A3411EBC2B9D1775CFC<line>p_4: 0x00000000000000000000000000000000<line>p_5: 0N<line>p_6: 0N<line>p_7: 0N<line>p_8: 0N<line>p_9: 40210601131610<line>p_10: 40210601131610<line>p_11: 0x90DBA4BF015B9A3411EBC2B9D1775CFC<line>p_12: 40210701000000<line>p_13: 0x00000000000000000000000000000000<line>p_14: 0x90DBA4BF015B9A3411EBC2B9D1775CFC<line>p_15: 0x90DBA4BF015B9A3411EBC2B9D1775CFC<line>p_16: 0x00000000000000000000000000000000<line>,Rows=0,RowsAffected=8,Context=<line>ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 9150 : ВыполнитьСписание(СтруктураПараметров, ТаблицаСписания, ДокументМоментВремени, Останавливаться, ПроведениеОстановлено);<line> ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 7863 : ПолучитьОстатки(СтруктураПараметров, ТаблицаСписания, МоментКон, Организация);<line> ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 7465 : ПолучитьОстаткиУпр(СтруктураПараметров, ТаблицаСписания, МоментКон);<line> ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 11639 : ПолучитьОстаткиПартийНаСкладахУпр(СтруктураПараметров, МоментКон);<line> ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 11420 : СтруктураПараметров.Вставить("ДеревоПартииТоваровНаСкладахУпр",<line> ОбщийМодуль.УправлениеЗапасамиПартионныйУчет.Модуль : 9785 : Возврат Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);<line>
То есть тормоза находятся в общем модуле УправлениеЗапасамиПартионныйУчет.
Конечная строка: Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
Из данных строк ТЖ получаем, что на СУБД внезапно запрос выполняется медленно. Но что может вызывать такое непредсказуемое поведение?
Через Extended Events собраны планы запросов (к сожалению, уже не сохранились, приложить нечего), планы генерируются распараллеленными, поиск в таблицах выполняется по индексам, ничего особенно, но как-то медленно происходят соединения таблиц (60-90% времени), причем такие замедления бывают иногда, а часто в основном быстро без задержек.
Остается смотреть текст запроса:
Процедура ЗаполнитьЗапросПартийНаСкладахУпр
Процедура ЗаполнитьЗапросПартийНаСкладахУпр(Запрос, ВестиПартионныйУчетПоСкладам, СтратегияСтатусПартии, СпособОценкиМПЗ)
ПараметрыЗапроса_ДатаОприходования = ПолучитьДанныеДляЗапроса_ДокументОприходованияДата(СпособОценкиМПЗ,"ПартииТоваровНаСкладах");
Запрос.Текст =
"ВЫБРАТЬ
| ПартииТоваровНаСкладах.Номенклатура,
| ПартииТоваровНаСкладах.ДокументОприходования,
| ПартииТоваровНаСкладах.Склад,
| ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры,
| ПартииТоваровНаСкладах.СерияНоменклатуры,
| ПартииТоваровНаСкладах.Качество,
| ПартииТоваровНаСкладах.Заказ,
| ПартииТоваровНаСкладах.КоличествоОстаток КАК Количество,
| ПартииТоваровНаСкладах.СтоимостьОстаток КАК Стоимость,
| ПартииТоваровНаСкладах.СтатусПартии
|
|ПОМЕСТИТЬ ПартииТоваровНаСкладах
|ИЗ
| РегистрНакопления.ПартииТоваровНаСкладах.Остатки(
| &Дат,
| Организация = &Организация
| И (Номенклатура, ХарактеристикаНоменклатуры) В (
| ВЫБРАТЬ
| СписанныеТовары.Номенклатура,
| СписанныеТовары.ХарактеристикаНоменклатуры
| ИЗ
| РегистрСведений.СписанныеТовары КАК СписанныеТовары
| ГДЕ
| СписанныеТовары.Регистратор = &Ссылка)" + ?(ВестиПартионныйУчетПоСкладам, "
| И (Склад В (
| ВЫБРАТЬ
| РегистрСведений.СписанныеТовары.Склад
| ИЗ
| РегистрСведений.СписанныеТовары
| ГДЕ
| РегистрСведений.СписанныеТовары.Регистратор = &Ссылка)
| ИЛИ Склад = &ПустойСклад)", "") + ") КАК ПартииТоваровНаСкладах
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура,
| ХарактеристикаНоменклатуры,
| Качество,
| " + ?(ВестиПартионныйУчетПоСкладам, "Склад," , "") + "
| СтатусПартии,
| Заказ,
| СерияНоменклатуры
|;
|
|///////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СписанныеТовары.НомерСтрокиДокумента КАК НомерСтрокиДокумента,
| ПартииТоваровНаСкладах.Номенклатура,
| ПартииТоваровНаСкладах.ДокументОприходования,
| "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Выбор+"
| ПартииТоваровНаСкладах.Склад,
| ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры,
| ПартииТоваровНаСкладах.СерияНоменклатуры,
| ПартииТоваровНаСкладах.Качество,
| ПартииТоваровНаСкладах.Заказ,
| ПартииТоваровНаСкладах.Количество,
| ПартииТоваровНаСкладах.Стоимость,
| ПартииТоваровНаСкладах.СтатусПартии,
| ВЫБОР
| КОГДА СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры
| ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ КАК ЧислоСерияНоменклатуры,
| ВЫБОР
| КОГДА СписанныеТовары.ДокументПартии = НЕОПРЕДЕЛЕНО
| ТОГДА 0
| ИНАЧЕ ВЫБОР
| КОГДА СписанныеТовары.ДокументПартии = ПартииТоваровНаСкладах.ДокументОприходования
| ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ
| КОНЕЦ КАК ЧислоДокументОприходования,
| ВЫБОР
| КОГДА СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО
| ТОГДА 0
| ИНАЧЕ ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ
| КОНЕЦ КАК ЧислоЗаказ,
| ВЫБОР
| КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ КАК ЧислоСтатусПартии
|ИЗ
| РегистрСведений.СписанныеТовары КАК СписанныеТовары
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
| ПО
| СписанныеТовары.Номенклатура = ПартииТоваровНаСкладах.Номенклатура
| И СписанныеТовары.ХарактеристикаНоменклатуры = ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры
| И (ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Качество = &ПустоеКачество
| ТОГДА ИСТИНА
| ИНАЧЕ ВЫБОР
| КОГДА СписанныеТовары.Качество = &ПустоеКачество
| ТОГДА ПартииТоваровНаСкладах.Качество = &КачествоНовый
| ИНАЧЕ ПартииТоваровНаСкладах.Качество = СписанныеТовары.Качество
| КОНЕЦ
| КОНЕЦ)
| " + ?(ВестиПартионныйУчетПоСкладам, "И (ПартииТоваровНаСкладах.Склад = СписанныеТовары.Склад ИЛИ ПартииТоваровНаСкладах.Склад = &ПустойСклад)", "") + "
| И (ВЫБОР
| КОГДА СписанныеТовары.ДопустимыйСтатус1 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус2 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус3 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус4 <> &ПустойСтатус
| ТОГДА ПартииТоваровНаСкладах.СтатусПартии = &ПустойСтатус
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = &СтатусПартииПоОрдеру
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус1
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус2
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус3
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус4
| ИНАЧЕ ИСТИНА
| КОНЕЦ)
|
| И (ВЫБОР
| КОГДА СписанныеТовары.СписыватьТолькоПоЗаказу = ИСТИНА
| ТОГДА ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии
| ТОГДА ВЫБОР
| КОГДА (НЕ СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО)
| ТОГДА ЛОЖЬ
| ИНАЧЕ ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| КОНЕЦ
| ИНАЧЕ ИСТИНА
| КОНЕЦ
| ИНАЧЕ ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии
| ТОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| ИНАЧЕ ИСТИНА
| КОНЕЦ
| КОНЕЦ)
| И (СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры
| ИЛИ ПартииТоваровНаСкладах.СерияНоменклатуры = &ПустаяСерияНоменклатуры)
|ГДЕ
| СписанныеТовары.Регистратор = &ОсновнойДокумент
|
|УПОРЯДОЧИТЬ ПО
| ЧислоСерияНоменклатуры,
| ЧислоДокументОприходования,
| ЧислоЗаказ,
| ЧислоСтатусПартии" + ?(СтратегияСтатусПартии = Перечисления.СтретегииСписанияПартийТоваровПоСтатусам.СначалаПринятыеПотомСобственные, " Убыв", "") + ",
| "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Сортировка+"
| ДокументОприходования" + ?(СпособОценкиМПЗ = "ЛИФО", " Убыв","") + ",
| ПартииТоваровНаСкладах.Склад
|ИТОГИ ПО
| НомерСтрокиДокумента
|;
|
|///////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ ПартииТоваровНаСкладах";
КонецПроцедуры // ЗаполнитьЗапросПартийНаСкладахУпр()
В запросе остатки соединяются несколько раз с регистром СписанныеТовары.
Нужно отметить, что объем данных регистров достаточный:
РегистрНакопления.ПартииТоваровНаСкладах: 4 млн. строк.
РегистрСведений.СписанныеТовары: 3 млн. строк.
РегистрНакопления.ПартииТоваровНаСкладах.Итоги: 100 тыс. строк.
Казалось, что достаточно чаще пересчитывать итоги, и всегда будет использоваться только 100 тыс. строк, то есть только итоги вместо основного регистра, но функциональная специфика работы организации такова, что старые периоды редактируют почти каждый день много раз, поэтому в запросе используется именно основной регистр с 4 млн. строк.
И вот мы представляем, как 4 млн. строк соединяются с 3 млн. строк несколько раз, пусть даже по индексам, но поиск по таким объемам уже дает о себе знать, и похоже в этом и причина.
И тогда я задался мыслью: а зачем каждый раз обращаться в регистр СписанныеТовары, если товаров всего пару десятков для РТУ и их можно получить всего один раз для дальнейшего использования! И такая временная таблица уже даже присутствует в программном коде — это переменная ТаблицаСписания, которую достаточно передать в запрос в качестве параметра.
И теперь можно перестроить запрос с использованием существующей таблицы, избавившись от многоразового подтягивания многомилионного регистра СписанныеТовары.
Процедура КС_ЗаполнитьЗапросПартийНаСкладахУпр_РТУ
Процедура КС_ЗаполнитьЗапросПартийНаСкладахУпр_РТУ(Запрос, ВестиПартионныйУчетПоСкладам, СтратегияСтатусПартии, СпособОценкиМПЗ)
ПараметрыЗапроса_ДатаОприходования = ПолучитьДанныеДляЗапроса_ДокументОприходованияДата(СпособОценкиМПЗ,"ПартииТоваровНаСкладах");
Запрос.Текст =
"Выбрать
|Т.Активность
|,Т.ВалютаДокумента
|,Т.ВедениеУчетаПоПроектам
//|,Т.ВестиПартионныйУчетПоСериям
|,Т.ВидТабличнойЧасти
|,Т.ДоговорКонтрагента
|,Т.ДокументОприходованияНовый
|,Т.ДокументПартии
|,Т.Регистратор
|,Т.Склад
|,Т.Номенклатура
|,Т.ХарактеристикаНоменклатуры
|,Т.Качество
|,Т.ДопустимыйСтатус1
|,Т.ДопустимыйСтатус2
|,Т.ДопустимыйСтатус3
|,Т.ДопустимыйСтатус4
|,Т.СписыватьТолькоПоЗаказу
|,Т.СерияНоменклатуры
|,Т.ЗаказПартии
|,Т.НомерСтрокиДокумента
|,Т.КодОперацииПартииТоваров
|ПОМЕСТИТЬ ТаблицаСписания
|ИЗ &ТаблицаСписания КАК Т
//|ГДЕ Т.Регистратор = &Ссылка
|
|;
|ВЫБРАТЬ
| ПартииТоваровНаСкладах.Номенклатура,
| ПартииТоваровНаСкладах.ДокументОприходования,
| ПартииТоваровНаСкладах.Склад,
| ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры,
| ПартииТоваровНаСкладах.СерияНоменклатуры,
| ПартииТоваровНаСкладах.Качество,
| ПартииТоваровНаСкладах.Заказ,
| ПартииТоваровНаСкладах.КоличествоОстаток КАК Количество,
| ПартииТоваровНаСкладах.СтоимостьОстаток КАК Стоимость,
| ПартииТоваровНаСкладах.СтатусПартии
|
|ПОМЕСТИТЬ ПартииТоваровНаСкладах
|ИЗ
| РегистрНакопления.ПартииТоваровНаСкладах.Остатки(
| &Дат,
| Организация = &Организация
| И (Номенклатура, ХарактеристикаНоменклатуры) В (
| ВЫБРАТЬ
| СписанныеТовары.Номенклатура,
| СписанныеТовары.ХарактеристикаНоменклатуры
| ИЗ
| ТаблицаСписания КАК СписанныеТовары
| )" + ?(ВестиПартионныйУчетПоСкладам, "
| И (Склад В (
| ВЫБРАТЬ СписанныеТовары.Склад ИЗ ТаблицаСписания КАК СписанныеТовары)
| ИЛИ Склад = &ПустойСклад)", "") + ") КАК ПартииТоваровНаСкладах
|
//|ИНДЕКСИРОВАТЬ ПО
//| Номенклатура,
//| ХарактеристикаНоменклатуры,
//| Качество,
//| " + ?(ВестиПартионныйУчетПоСкладам, "Склад," , "") + "
//| СтатусПартии,
//| Заказ,
//| СерияНоменклатуры
|;
|
|///////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СписанныеТовары.НомерСтрокиДокумента КАК НомерСтрокиДокумента,
| ПартииТоваровНаСкладах.Номенклатура,
| ПартииТоваровНаСкладах.ДокументОприходования,
| "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Выбор+"
| ПартииТоваровНаСкладах.Склад,
| ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры,
| ПартииТоваровНаСкладах.СерияНоменклатуры,
| ПартииТоваровНаСкладах.Качество,
| ПартииТоваровНаСкладах.Заказ,
| ПартииТоваровНаСкладах.Количество,
| ПартииТоваровНаСкладах.Стоимость,
| ПартииТоваровНаСкладах.СтатусПартии,
| ВЫБОР
| КОГДА СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры
| ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ КАК ЧислоСерияНоменклатуры,
| ВЫБОР
| КОГДА СписанныеТовары.ДокументПартии = НЕОПРЕДЕЛЕНО
| ТОГДА 0
| ИНАЧЕ ВЫБОР
| КОГДА СписанныеТовары.ДокументПартии = ПартииТоваровНаСкладах.ДокументОприходования
| ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ
| КОНЕЦ КАК ЧислоДокументОприходования,
| ВЫБОР
| КОГДА СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО
| ТОГДА 0
| ИНАЧЕ ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ
| КОНЕЦ КАК ЧислоЗаказ,
| ВЫБОР
| КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ КАК ЧислоСтатусПартии
|ИЗ
| ТаблицаСписания КАК СписанныеТовары
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
| ПО
| СписанныеТовары.Номенклатура = ПартииТоваровНаСкладах.Номенклатура
| И СписанныеТовары.ХарактеристикаНоменклатуры = ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры
| И (ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Качество = &ПустоеКачество
| ТОГДА ИСТИНА
| ИНАЧЕ ВЫБОР
| КОГДА СписанныеТовары.Качество = &ПустоеКачество
| ТОГДА ПартииТоваровНаСкладах.Качество = &КачествоНовый
| ИНАЧЕ ПартииТоваровНаСкладах.Качество = СписанныеТовары.Качество
| КОНЕЦ
| КОНЕЦ)
| " + ?(ВестиПартионныйУчетПоСкладам, "И (ПартииТоваровНаСкладах.Склад = СписанныеТовары.Склад ИЛИ ПартииТоваровНаСкладах.Склад = &ПустойСклад)", "") + "
| И (ВЫБОР
| КОГДА СписанныеТовары.ДопустимыйСтатус1 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус2 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус3 <> &ПустойСтатус
| ИЛИ СписанныеТовары.ДопустимыйСтатус4 <> &ПустойСтатус
| ТОГДА ПартииТоваровНаСкладах.СтатусПартии = &ПустойСтатус
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = &СтатусПартииПоОрдеру
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус1
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус2
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус3
| ИЛИ ПартииТоваровНаСкладах.СтатусПартии = СписанныеТовары.ДопустимыйСтатус4
| ИНАЧЕ ИСТИНА
| КОНЕЦ)
|
| И (ВЫБОР
| КОГДА СписанныеТовары.СписыватьТолькоПоЗаказу = ИСТИНА
| ТОГДА ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии
| ТОГДА ВЫБОР
| КОГДА (НЕ СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО)
| ТОГДА ЛОЖЬ
| ИНАЧЕ ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| КОНЕЦ
| ИНАЧЕ ИСТИНА
| КОНЕЦ
| ИНАЧЕ ВЫБОР
| КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии
| ТОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ
| ИНАЧЕ ИСТИНА
| КОНЕЦ
| КОНЕЦ)
| И (СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры
| ИЛИ ПартииТоваровНаСкладах.СерияНоменклатуры = &ПустаяСерияНоменклатуры)
|
|УПОРЯДОЧИТЬ ПО
| ЧислоСерияНоменклатуры,
| ЧислоДокументОприходования,
| ЧислоЗаказ,
| ЧислоСтатусПартии" + ?(СтратегияСтатусПартии = Перечисления.СтретегииСписанияПартийТоваровПоСтатусам.СначалаПринятыеПотомСобственные, " Убыв", "") + ",
| "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Сортировка+"
| ДокументОприходования" + ?(СпособОценкиМПЗ = "ЛИФО", " Убыв","") + ",
| ПартииТоваровНаСкладах.Склад
|ИТОГИ ПО
| НомерСтрокиДокумента
|;
|
|///////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ ПартииТоваровНаСкладах
|;УНИЧТОЖИТЬ ТаблицаСписания";
КонецПроцедуры // КС_ЗаполнитьЗапросПартийНаСкладахУпр_РТУ()
Нужно еще отметить: так как данная оптимизация значительно затрагивает функционал, повышает риск сбоев в остатках, то пришлось потратить некоторое время на проверку после массового проведения документов. Сверяли остатки двух баз: остатки до с остатками после оптимизации.
Итог
За день проводится около 200 РТУ.
Результаты до оптимизации: каждая пятая РТУ проводилась около минуты-полторы, каждая третья РТУ проводилась около 15-30 секунд.
Результат после оптимизации: за день около 2-3 РТУ проводится в течение 20 секунд, остальные проводятся менее 10 секунд.
Результаты проведения других документов не проверял, но суть та же.