База знаний Одина — Одинэсника › Форумы › ODIN — Форум по 1С Предприятию › Как оптимизировать функцию РассчитатьНормуНаСервере в 1С?
- В этой теме 1 ответ, 2 участника, последнее обновление 3 дня, 19 часов назад сделано
Odin — Одинэсник.
-
АвторСообщения
-
-
30 апреля 2026 в 20:14 #36913
&НаСервере
Функция РассчитатьНормуНаСервере(мсДанные, Дата)
МассивДокумента = Новый Массив;
ТаблицаПараметров = Новый ТаблицаЗначений;
ТаблицаПараметров.Колонки.Добавить(«НомерСтроки», Новый ОписаниеТипов(«Число»));
ТаблицаПараметров.Колонки.Добавить(«ВидДоговора», Новый ОписаниеТипов(«ПеречислениеСсылка.ВидыДоговоровКонтрагентов»));
ТаблицаПараметров.Колонки.Добавить(«Договор», Новый ОписаниеТипов(«СправочникСсылка.ДоговорыКонтрагентов»));
ТаблицаПараметров.Колонки.Добавить(«Страна», Новый ОписаниеТипов(«СправочникСсылка.КлассификаторСтранМира»));
ТаблицаПараметров.Колонки.Добавить(«Услуга», Новый ОписаниеТипов(«ДокументСсылка.ПоступлениеТоваровУслуг»));
Для Каждого СтрМс Из мсДанные Цикл
НоваяСтрока = ТаблицаПараметров.Добавить();
НоваяСтрока.НомерСтроки = СтрМс.НомерСтроки;
НоваяСтрока.ВидДоговора = СтрМс.ВидДоговора;
НоваяСтрока.Договор = СтрМс.Договор;
НоваяСтрока.Страна = СтрМс.Страна;
НоваяСтрока.Услуга = СтрМс.Услуга;
КонецЦикла;Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| ТаблицаПараметров.ВидДоговора КАК ВидДоговора,
| ТаблицаПараметров.Договор КАК Договор,
| ТаблицаПараметров.Страна КАК Страна,
| ТаблицаПараметров.Услуга КАК Услуга,
| ТаблицаПараметров.НомерСтроки КАК НомерСтроки
|ПОМЕСТИТЬ ВТ_Параметры
|ИЗ
| &ТаблицаПараметров КАК ТаблицаПараметров
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПриказОНормах.Ссылка КАК Ссылка,
| ПриказОНормах.Дата КАК Дата
|ПОМЕСТИТЬ ВТ_ПоследнийПриказ
|ИЗ
| Документ.ПриказОНормах КАК ПриказОНормах
|ГДЕ
| ПриказОНормах.Дата В
| (ВЫБРАТЬ
| МАКСИМУМ(ПриказВнут.Дата)
| ИЗ
| Документ.ПриказОНормах КАК ПриказВнут
| ГДЕ
| ПриказВнут.Дата <= &Дата)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Параметры.НомерСтроки КАК НомерСтроки,
| ВТ_Параметры.ВидДоговора КАК ВидДоговора,
| ВТ_Параметры.Договор КАК Договор,
| ВТ_Параметры.Страна КАК Страна,
| ВТ_Параметры.Услуга КАК Услуга,
| ПриказОНормахНормы.Норма КАК Норма,
| ВЫБОР
| КОГДА ПриказОНормахНормы.ВидДоговора = ВТ_Параметры.ВидДоговора
| ТОГДА 1
| КОГДА ПриказОНормахНормы.ВидДоговора = ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка)
| ТОГДА 2
| ИНАЧЕ 3
| КОНЕЦ + ВЫБОР
| КОГДА ПриказОНормахНормы.Договор = ВТ_Параметры.Договор
| ТОГДА 1
| КОГДА ПриказОНормахНормы.Договор = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
| ТОГДА 2
| ИНАЧЕ 3
| КОНЕЦ + ВЫБОР
| КОГДА ПриказОНормахНормы.Страна = ВТ_Параметры.Страна
| ТОГДА 1
| КОГДА ПриказОНормахНормы.Страна = ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка)
| ТОГДА 2
| ИНАЧЕ 3
| КОНЕЦ + ВЫБОР
| КОГДА ПриказОНормахНормы.Услуга = ВТ_Параметры.Услуга
| ТОГДА 1
| КОГДА ПриказОНормахНормы.Услуга = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
| ТОГДА 2
| ИНАЧЕ 3
| КОНЕЦ КАК СуммаВесов,
| ВТ_ПоследнийПриказ.Дата КАК Дата,
| ВЫБОР
| КОГДА ПриказОНормахНормы.ВидДоговора <> ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка)
| ТОГДА 4
| КОГДА ПриказОНормахНормы.Договор <> ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
| ТОГДА 3
| КОГДА ПриказОНормахНормы.Страна <> ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка)
| ТОГДА 2
| КОГДА ПриказОНормахНормы.Услуга <> ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ КАК Приоритет
|ПОМЕСТИТЬ ВТ_НормыСВесами
|ИЗ
| ВТ_Параметры КАК ВТ_Параметры
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриказОНормах.Нормы КАК ПриказОНормахНормы
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследнийПриказ КАК ВТ_ПоследнийПриказ
| ПО ПриказОНормахНормы.Ссылка = ВТ_ПоследнийПриказ.Ссылка
| ПО (ПриказОНормахНормы.ВидДоговора = ВТ_Параметры.ВидДоговора
| ИЛИ ПриказОНормахНормы.ВидДоговора = ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка))
| И (ПриказОНормахНормы.Договор = ВТ_Параметры.Договор
| ИЛИ ПриказОНормахНормы.Договор = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка))
| И (ПриказОНормахНормы.Страна = ВТ_Параметры.Страна
| ИЛИ ПриказОНормахНормы.Страна = ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка))
| И (ПриказОНормахНормы.Услуга = ВТ_Параметры.Услуга
| ИЛИ ПриказОНормахНормы.Услуга = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка))
|ГДЕ
| ПриказОНормахНормы.Норма > -1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
| МАКСИМУМ(ВТ_НормыСВесами.Приоритет) КАК Приоритет
|ПОМЕСТИТЬ ВТ_МаксПриоритет
|ИЗ
| ВТ_НормыСВесами КАК ВТ_НормыСВесами
|
|СГРУППИРОВАТЬ ПО
| ВТ_НормыСВесами.НомерСтроки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
| МИНИМУМ(ВТ_НормыСВесами.СуммаВесов) КАК СуммаВесов
|ПОМЕСТИТЬ ВТ_МинСуммаВесов
|ИЗ
| ВТ_НормыСВесами КАК ВТ_НормыСВесами
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксПриоритет КАК ВТ_МаксПриоритет
| ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МаксПриоритет.НомерСтроки
| И ВТ_НормыСВесами.Приоритет = ВТ_МаксПриоритет.Приоритет
|
|СГРУППИРОВАТЬ ПО
| ВТ_НормыСВесами.НомерСтроки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
| ВТ_НормыСВесами.ВидДоговора КАК ВидДоговора,
| ВТ_НормыСВесами.Договор КАК Договор,
| ВТ_НормыСВесами.Страна КАК Страна,
| ВТ_НормыСВесами.Услуга КАК Услуга,
| ВТ_НормыСВесами.Норма КАК Норма,
| ВТ_НормыСВесами.СуммаВесов КАК СуммаВесов,
| ВТ_НормыСВесами.Дата КАК Дата,
| ВТ_НормыСВесами.Приоритет КАК Приоритет
|ИЗ
| ВТ_НормыСВесами КАК ВТ_НормыСВесами
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МинСуммаВесов КАК ВТ_МинСуммаВесов
| ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МинСуммаВесов.НомерСтроки
| И ВТ_НормыСВесами.СуммаВесов = ВТ_МинСуммаВесов.СуммаВесов
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксПриоритет КАК ВТ_МаксПриоритет
| ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МаксПриоритет.НомерСтроки
| И ВТ_НормыСВесами.Приоритет = ВТ_МаксПриоритет.Приоритет
|
|УПОРЯДОЧИТЬ ПО
| НомерСтроки»;Запрос.УстановитьПараметр(«Дата», КонецДня(Дата));
Запрос.УстановитьПараметр(«ТаблицаПараметров», ТаблицаПараметров);Результат = Запрос.Выполнить().Выгрузить();
Для Каждого Строка Из Результат Цикл
стСтрокаТаблицы = Новый Структура;
стСтрокаТаблицы.Вставить(«НомерСтроки», Строка.НомерСтроки);
стСтрокаТаблицы.Вставить(«ВидДоговора», Строка.ВидДоговора);
стСтрокаТаблицы.Вставить(«Договор», Строка.Договор);
стСтрокаТаблицы.Вставить(«Страна», Строка.Страна);
стСтрокаТаблицы.Вставить(«Услуга», Строка.Услуга);
стСтрокаТаблицы.Вставить(«Норма», Строка.Норма);
МассивДокумента.Добавить(стСтрокаТаблицы);
КонецЦикла;Возврат МассивДокумента;
КонецФункции оцени функцию на оптимальность -
30 апреля 2026 в 20:14 #36914
Функция сделана по правильной схеме — вместо цикла с отдельным запросом на каждую строку передаётся таблица значений (временный набор данных) и один запрос сразу обрабатывает все параметры. Это уже большой плюс с точки зрения производительности.
### Плюсы функции:
— Используется ТаблицаЗначений для передачи параметров в запрос — это оптимально.
— Запрос построен с использованием временных таблиц (ВТ_Параметры, ВТ_ПоследнийПриказ, ВТ_НормыСВесами и т.д.) — хорошо структурирован.
— Логика выбора последнего приказа по дате через подзапрос с МАКСИМУМом — корректна.
— Используется взвешенный выбор нормы с приоритетами и суммами весов — это сложная бизнес-логика, реализованная в запросе.
— Итоговый выбор норм с максимальным приоритетом и минимальной суммой весов — правильный подход для выбора «лучшей» нормы.### Что можно улучшить или проверить:
1. Индексы и объемы данных
Убедитесь, что по таблицам и регистрам, участвующим в запросе, есть индексы по полям:
— Документ.ПриказОНормах.Дата
— Документ.ПриказОНормах.Нормы.Ссылка
— Документ.ПриказОНормах.Нормы.ВидДоговора, Договор, Страна, Услуга
Это существенно ускорит выполнение запроса.2. Оптимизация подзапроса с МАКСИМУМОМ
Подзапрос для выбора последнего приказа по дате:MySQL1234ПриказОНормах.Дата В(ВЫБРАТЬ МАКСИМУМ(ПриказВнут.Дата)ИЗ Документ.ПриказОНормах КАК ПриказВнутГДЕ ПриказВнут.Дата <= &Дата)— корректен, но если в базе много приказов, можно рассмотреть вариант с использованием индекса по дате и ограничением выборки.
3. Использование ЛЕВОГО СОЕДИНЕНИЯ
В запросе:MySQL12345ВТ_Параметры КАК ВТ_ПараметрыЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриказОНормах.Нормы КАК ПриказОНормахНормыВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследнийПриказ КАК ВТ_ПоследнийПриказПО ПриказОНормахНормы.Ссылка = ВТ_ПоследнийПриказ.СсылкаПО ...— ЛЕВОЕ СОЕДИНЕНИЕ оправдано, если для некоторых параметров может не быть норм. Если всегда есть — можно заменить на ВНУТРЕННЕЕ.
4. Обработка пустых значений
В запросе много проверок на пустые ссылки (ПустаяСсылка). Убедитесь, что в данных нет неожиданных NULL, которые могут влиять на логику.5. Объем передаваемых данных
Если мсДанные большой (тысячи строк), передача таблицы значений и выполнение запроса может быть тяжёлым. В этом случае стоит подумать о пакетной обработке.6. Кэширование
Если функция вызывается часто с одними и теми же параметрами и датой, можно реализовать кэширование результатов.### Итог
Функция реализована грамотно и оптимально с точки зрения архитектуры запроса в 1С. Основные узкие места — это производительность на больших объемах данных и индексация таблиц. Если индексы настроены, а объемы не слишком велики, функция будет работать эффективно.
Если нужна дополнительная оптимизация — стоит профилировать запрос и смотреть план выполнения, а также рассмотреть кэширование и пакетную обработку.
-
-
АвторСообщения
- Для ответа в этой теме необходимо авторизоваться.