Проведение по партиям в 1С УПП в традиционном режиме (партионный учет, НЕ РАУЗ) довольно ресурсоемкая операция. В случае если база данных “вашего” предприятия приличных размеров и документов регистрируется очень много это может привести к существенной проблеме. В программном коде УПП есть одно узкое место, которое приводит к существенному увеличению времени выполнения данной операции при повышении объема базы данных.
Информация представленная ниже актуальна для версии УПП 1.3.60.3 и скорее всего для версий более ранних или более поздних, возможно с некоторыми изменениями.
Был произведен замер производительности при котором выявлено, что основной причиной замедления (до 90%) проведения по партиям является “ОбщийМодуль.УправлениеЗапасамиПартионныйУчет”, а именно 3 процедуры из секции “ПОЛУЧЕНИЕ ОСТАТКОВ ИЗ РЕГИСТРОВ ПАРТИЙ”:
1 2 3 |
Процедура ЗаполнитьЗапросПартийНаСкладахУпр(Запрос, ВестиПартионныйУчетПоСкладам, СтратегияСтатусПартии, СпособОценкиМПЗ) Процедура ЗаполнитьЗапросПартийНаСкладахБух(Запрос, ВестиПартионныйУчетПоСкладам, СтратегияСтатусПартии, СпособОценкиМПЗ) Процедура ЗаполнитьЗапросПартийНаСкладахНал(Запрос, ВестиПартионныйУчетПоСкладам, СтратегияСтатусПартии, СпособОценкиМПЗ) |
В начале этих 3-х процедур имеется текст запроса, которые нам понадобится изменить.
Для анализа возьмём запрос из процедуры “ЗаполнитьЗапросПартийНаСкладахБух()”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
Запрос.Текст = "ВЫБРАТЬ | ПартииТоваровНаСкладах.ДокументОприходования, | ПартииТоваровНаСкладах.ДокументОприходования.Представление КАК ДокументОприходованияПредставление, | ПартииТоваровНаСкладах.Номенклатура, | ПартииТоваровНаСкладах.Склад, | ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры, | ПартииТоваровНаСкладах.СерияНоменклатуры, | ПартииТоваровНаСкладах.Качество, | ПартииТоваровНаСкладах.Заказ, | ПартииТоваровНаСкладах.КоличествоОстаток КАК Количество, | ПартииТоваровНаСкладах.СтоимостьОстаток КАК Стоимость, | ПартииТоваровНаСкладах.СчетУчета, | ПартииТоваровНаСкладах.Организация | |ПОМЕСТИТЬ ПартииТоваровНаСкладах |ИЗ | РегистрНакопления.ПартииТоваровНаСкладахБухгалтерскийУчет.Остатки( | &Дат, | Организация = &Организация | И (Номенклатура, ХарактеристикаНоменклатуры) В ( | ВЫБРАТЬ | СписанныеТовары.Номенклатура, | СписанныеТовары.ХарактеристикаНоменклатуры | ИЗ | РегистрСведений.СписанныеТовары КАК СписанныеТовары | ГДЕ | СписанныеТовары.Регистратор = &Ссылка)" + ?(ВестиПартионныйУчетПоСкладам, " | И (Склад В ( | ВЫБРАТЬ | СписанныеТовары.Склад | ИЗ | РегистрСведений.СписанныеТовары КАК СписанныеТовары | ГДЕ | СписанныеТовары.Регистратор = &Ссылка) | ИЛИ Склад = &ПустойСклад)", "") + ") КАК ПартииТоваровНаСкладах | |ИНДЕКСИРОВАТЬ ПО | Организация, | Номенклатура, | ХарактеристикаНоменклатуры, | Качество, | " + ?(ВестиПартионныйУчетПоСкладам, "Склад," , "") + " | СчетУчета, | Заказ, | СерияНоменклатуры |; | |/////////////////////////////////////////////////////////////////////////////// |ВЫБРАТЬ | СписанныеТовары.НомерСтрокиДокумента КАК НомерСтрокиДокумента, | ПартииТоваровНаСкладах.ДокументОприходования, | ПартииТоваровНаСкладах.ДокументОприходованияПредставление, | "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Выбор+" | ПартииТоваровНаСкладах.Номенклатура, | ПартииТоваровНаСкладах.Склад, | ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры, | ПартииТоваровНаСкладах.СерияНоменклатуры, | ПартииТоваровНаСкладах.Качество, | ПартииТоваровНаСкладах.Заказ, | ПартииТоваровНаСкладах.Количество, | ПартииТоваровНаСкладах.Стоимость, | ПартииТоваровНаСкладах.СчетУчета, | ПартииТоваровНаСкладах.Организация, | ВЫБОР | КОГДА СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры | ТОГДА 0 | ИНАЧЕ 1 | КОНЕЦ КАК ЧислоСерияНоменклатуры, | ВЫБОР | КОГДА ПартииТоваровНаСкладах.СчетУчета = СписанныеТовары.ПринятыеСчетУчетаБУ | ТОГДА 1 | ИНАЧЕ 0 | КОНЕЦ КАК ЧислоСтатусПартии, | ВЫБОР | КОГДА СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО | ТОГДА 0 | ИНАЧЕ ВЫБОР | КОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ | ТОГДА 1 | ИНАЧЕ 0 | КОНЕЦ | КОНЕЦ КАК ЧислоЗаказ, | ВЫБОР | КОГДА СписанныеТовары.ДокументПартии = НЕОПРЕДЕЛЕНО | ТОГДА 0 | ИНАЧЕ ВЫБОР | КОГДА СписанныеТовары.ДокументПартии = ПартииТоваровНаСкладах.ДокументОприходования | ТОГДА 0 | ИНАЧЕ 1 | КОНЕЦ | КОНЕЦ КАК ЧислоДокументОприходования |ИЗ | РегистрСведений.СписанныеТовары КАК СписанныеТовары | ВНУТРЕННЕЕ СОЕДИНЕНИЕ | ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах | ПО | ПартииТоваровНаСкладах.Организация = СписанныеТовары.Организация | И ПартииТоваровНаСкладах.Номенклатура = СписанныеТовары.Номенклатура | И ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры = СписанныеТовары.ХарактеристикаНоменклатуры | И (ВЫБОР | КОГДА ПартииТоваровНаСкладах.Качество = &ПустоеКачество | ТОГДА ИСТИНА | ИНАЧЕ ВЫБОР | КОГДА СписанныеТовары.Качество = &ПустоеКачество | ТОГДА ПартииТоваровНаСкладах.Качество = &КачествоНовый | ИНАЧЕ ПартииТоваровНаСкладах.Качество = СписанныеТовары.Качество | КОНЕЦ | КОНЕЦ) | " + ?(ВестиПартионныйУчетПоСкладам, "И (ПартииТоваровНаСкладах.Склад = СписанныеТовары.Склад ИЛИ ПартииТоваровНаСкладах.Склад = &ПустойСклад)", "") + " | И (ПартииТоваровНаСкладах.СчетУчета = СписанныеТовары.СчетУчетаБУ | ИЛИ ПартииТоваровНаСкладах.СчетУчета = СписанныеТовары.ПринятыеСчетУчетаБУ) | И (ВЫБОР | КОГДА СписанныеТовары.СписыватьТолькоПоЗаказу = ИСТИНА | ТОГДА ВЫБОР | КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии | ТОГДА ВЫБОР | КОГДА (НЕ СписанныеТовары.ЗаказПартии = НЕОПРЕДЕЛЕНО) | ТОГДА ЛОЖЬ | ИНАЧЕ ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ | КОНЕЦ | ИНАЧЕ ИСТИНА | КОНЕЦ | ИНАЧЕ ВЫБОР | КОГДА ПартииТоваровНаСкладах.Заказ <> СписанныеТовары.ЗаказПартии | ТОГДА ПартииТоваровНаСкладах.Заказ = &ПустойЗаказ | ИНАЧЕ ИСТИНА | КОНЕЦ | КОНЕЦ) | И (СписанныеТовары.СерияНоменклатуры = ПартииТоваровНаСкладах.СерияНоменклатуры | ИЛИ ПартииТоваровНаСкладах.СерияНоменклатуры = &ПустаяСерияНоменклатуры) |ГДЕ | СписанныеТовары.Регистратор = &ОсновнойДокумент | |УПОРЯДОЧИТЬ ПО | ЧислоСерияНоменклатуры, | ЧислоДокументОприходования, | ЧислоЗаказ, | ЧислоСтатусПартии" + ?(СтратегияСтатусПартии = Перечисления.СтретегииСписанияПартийТоваровПоСтатусам.СначалаПринятыеПотомСобственные, " Убыв", "") + ", | "+ПараметрыЗапроса_ДатаОприходования.ДокОприходованияДата_Сортировка+" | ДокументОприходования" + ?(СпособОценкиМПЗ = "ЛИФО", " Убыв","") + ", | ПартииТоваровНаСкладах.Склад |ИТОГИ ПО | НомерСтрокиДокумента |; | |/////////////////////////////////////////////////////////////////////////////// |УНИЧТОЖИТЬ ПартииТоваровНаСкладах"; |
Т.к. данный модуль писали ещё наши дедушки 🙂 имеем следующие проблемы:
Проблема 1:
Сразу бросается в глаза использование вложенных запросов, что не лучшим образом скажется на производительности данных запросов. Пример:
1 2 3 4 5 6 7 8 |
| И (Номенклатура, ХарактеристикаНоменклатуры) В ( | ВЫБРАТЬ | СписанныеТовары.Номенклатура, | СписанныеТовары.ХарактеристикаНоменклатуры | ИЗ | РегистрСведений.СписанныеТовары КАК СписанныеТовары | ГДЕ | СписанныеТовары.Регистратор = &Ссылка) |
Необходимо перенести код вложенных запросов во временные таблицы.
Проблема 2:
Использование ВНУТРЕННЕГО СОЕДИНЕНИЯ с последующей фильтрацией данных:
1 2 3 4 5 6 |
| РегистрСведений.СписанныеТовары КАК СписанныеТовары | ВНУТРЕННЕЕ СОЕДИНЕНИЕ | ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах ... |ГДЕ | СписанныеТовары.Регистратор = &ОсновнойДокумент |
Нетрудно представить, что будет со скоростью выполнения данного кода в случае разрастания базы данных.
Требуется внести РегистрСведений.СписанныеТовары во временную таблицу, проиндексировать часть полей и отфильтровать заранее до внутреннего соединения!
Решено изменить код запроса с использованием временных таблиц. Не стану приводить здесь код, добавлю только ссылку на измененные процедуры.
В результате изменения в моем случае прирост скорости проведения по партиям составил, примерно в 5-7 раз.
Пример из жизни выполнения обработки “Проведение по партиям”:
Результаты до изменения кода: за 7 часов работы обработки “проведение по партиям” обрабатывалось 7-8 дней, т.е. около 1 часа на 1 день.
Результаты после изменения кода: за 3.5 часа работы обработки “проведение по партиям” обрабатывалось 26 дней, т.е. около 0.13 часа на 1 день.
Зависимость от размера БД здесь явная. Если ваша база данных ещё не имеет больших размеров, прирост может оказаться не столь внушительным.
Аналогичным образом можно исправить и другие процедуры данного модуля в секции:
1 2 |
//////////////////////////////////////////////////////////////////////////////// // ПОЛУЧЕНИЕ ОСТАТКОВ ИЗ РЕГИСТРОВ ПАРТИЙ |
такие как:
- Процедура ЗаполнитьЗапросПартийНаСкладахДляЗакрытияЗаказовПокупателей() (аналогично для БУХ, НАЛ)
- ЗаполнитьЗапросПартийНаСкладахДляОтложеннойОтгрузкиУпр() (аналогично для БУХ, НАЛ)
- ЗаполнитьЗапросПартийНаСкладахДляСписанияПоОрдернойСхемеУпр() (аналогично для БУХ, НАЛ)
- и т.д.
Вконце статьи как и обещал ссылка на текст измененных процедур данной статьи общего модуля “УправлениеЗапасамиПартионныйУчет”:
Общий модуль УправлениеЗапасамиПартионныйУчет_ Модуль
Сергей
15.12.2016 в 17:36 /
посмотрите на infostarte, там намного корректней эти процедуры изменены и универсальнее
admin
23.03.2017 в 06:43 /
Спасибо. Я уверен в том, что на infostart реализации более универсальные и правильные, там много интересного и я зарегистрирован там, некоторые свои работы так же выкладываю. Данный пример ускорения был реализован прилично давно без оглядки на какие-то другие ресурсы, т.к. требовалось оперативное вмешательство, т.к. проведение партий перестало укладываться в ночной запуск. Замером производительности в конфигураторе были обнаружены эти недостатки.