Как оптимизировать функцию РассчитатьНормуНаСервере в 1С?

База знаний Одина — Одинэсника Форумы ODIN — Форум по 1С Предприятию Как оптимизировать функцию РассчитатьНормуНаСервере в 1С?

Просмотр 1 ветки ответов
  • Автор
    Сообщения
    • #36913
      Фото аватараOdineski
      Участник

        &НаСервере
        Функция РассчитатьНормуНаСервере(мсДанные, Дата)
        МассивДокумента = Новый Массив;
        ТаблицаПараметров = Новый ТаблицаЗначений;
        ТаблицаПараметров.Колонки.Добавить(«НомерСтроки», Новый ОписаниеТипов(«Число»));
        ТаблицаПараметров.Колонки.Добавить(«ВидДоговора», Новый ОписаниеТипов(«ПеречислениеСсылка.ВидыДоговоровКонтрагентов»));
        ТаблицаПараметров.Колонки.Добавить(«Договор», Новый ОписаниеТипов(«СправочникСсылка.ДоговорыКонтрагентов»));
        ТаблицаПараметров.Колонки.Добавить(«Страна», Новый ОписаниеТипов(«СправочникСсылка.КлассификаторСтранМира»));
        ТаблицаПараметров.Колонки.Добавить(«Услуга», Новый ОписаниеТипов(«ДокументСсылка.ПоступлениеТоваровУслуг»));
        Для Каждого СтрМс Из мсДанные Цикл
        НоваяСтрока = ТаблицаПараметров.Добавить();
        НоваяСтрока.НомерСтроки = СтрМс.НомерСтроки;
        НоваяСтрока.ВидДоговора = СтрМс.ВидДоговора;
        НоваяСтрока.Договор = СтрМс.Договор;
        НоваяСтрока.Страна = СтрМс.Страна;
        НоваяСтрока.Услуга = СтрМс.Услуга;
        КонецЦикла;

        Запрос = Новый Запрос;
        Запрос.Текст =
        «ВЫБРАТЬ
        | ТаблицаПараметров.ВидДоговора КАК ВидДоговора,
        | ТаблицаПараметров.Договор КАК Договор,
        | ТаблицаПараметров.Страна КАК Страна,
        | ТаблицаПараметров.Услуга КАК Услуга,
        | ТаблицаПараметров.НомерСтроки КАК НомерСтроки
        |ПОМЕСТИТЬ ВТ_Параметры
        |ИЗ
        | &ТаблицаПараметров КАК ТаблицаПараметров
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ПриказОНормах.Ссылка КАК Ссылка,
        | ПриказОНормах.Дата КАК Дата
        |ПОМЕСТИТЬ ВТ_ПоследнийПриказ
        |ИЗ
        | Документ.ПриказОНормах КАК ПриказОНормах
        |ГДЕ
        | ПриказОНормах.Дата В
        | (ВЫБРАТЬ
        | МАКСИМУМ(ПриказВнут.Дата)
        | ИЗ
        | Документ.ПриказОНормах КАК ПриказВнут
        | ГДЕ
        | ПриказВнут.Дата <= &Дата)
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ВТ_Параметры.НомерСтроки КАК НомерСтроки,
        | ВТ_Параметры.ВидДоговора КАК ВидДоговора,
        | ВТ_Параметры.Договор КАК Договор,
        | ВТ_Параметры.Страна КАК Страна,
        | ВТ_Параметры.Услуга КАК Услуга,
        | ПриказОНормахНормы.Норма КАК Норма,
        | ВЫБОР
        | КОГДА ПриказОНормахНормы.ВидДоговора = ВТ_Параметры.ВидДоговора
        | ТОГДА 1
        | КОГДА ПриказОНормахНормы.ВидДоговора = ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка)
        | ТОГДА 2
        | ИНАЧЕ 3
        | КОНЕЦ + ВЫБОР
        | КОГДА ПриказОНормахНормы.Договор = ВТ_Параметры.Договор
        | ТОГДА 1
        | КОГДА ПриказОНормахНормы.Договор = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
        | ТОГДА 2
        | ИНАЧЕ 3
        | КОНЕЦ + ВЫБОР
        | КОГДА ПриказОНормахНормы.Страна = ВТ_Параметры.Страна
        | ТОГДА 1
        | КОГДА ПриказОНормахНормы.Страна = ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка)
        | ТОГДА 2
        | ИНАЧЕ 3
        | КОНЕЦ + ВЫБОР
        | КОГДА ПриказОНормахНормы.Услуга = ВТ_Параметры.Услуга
        | ТОГДА 1
        | КОГДА ПриказОНормахНормы.Услуга = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
        | ТОГДА 2
        | ИНАЧЕ 3
        | КОНЕЦ КАК СуммаВесов,
        | ВТ_ПоследнийПриказ.Дата КАК Дата,
        | ВЫБОР
        | КОГДА ПриказОНормахНормы.ВидДоговора <> ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка)
        | ТОГДА 4
        | КОГДА ПриказОНормахНормы.Договор <> ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
        | ТОГДА 3
        | КОГДА ПриказОНормахНормы.Страна <> ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка)
        | ТОГДА 2
        | КОГДА ПриказОНормахНормы.Услуга <> ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
        | ТОГДА 1
        | ИНАЧЕ 0
        | КОНЕЦ КАК Приоритет
        |ПОМЕСТИТЬ ВТ_НормыСВесами
        |ИЗ
        | ВТ_Параметры КАК ВТ_Параметры
        | ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриказОНормах.Нормы КАК ПриказОНормахНормы
        | ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследнийПриказ КАК ВТ_ПоследнийПриказ
        | ПО ПриказОНормахНормы.Ссылка = ВТ_ПоследнийПриказ.Ссылка
        | ПО (ПриказОНормахНормы.ВидДоговора = ВТ_Параметры.ВидДоговора
        | ИЛИ ПриказОНормахНормы.ВидДоговора = ЗНАЧЕНИЕ(Перечисление.ВидыДоговоровКонтрагентов.ПустаяСсылка))
        | И (ПриказОНормахНормы.Договор = ВТ_Параметры.Договор
        | ИЛИ ПриказОНормахНормы.Договор = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка))
        | И (ПриказОНормахНормы.Страна = ВТ_Параметры.Страна
        | ИЛИ ПриказОНормахНормы.Страна = ЗНАЧЕНИЕ(Справочник.КлассификаторСтранМира.ПустаяСсылка))
        | И (ПриказОНормахНормы.Услуга = ВТ_Параметры.Услуга
        | ИЛИ ПриказОНормахНормы.Услуга = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка))
        |ГДЕ
        | ПриказОНормахНормы.Норма > -1
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
        | МАКСИМУМ(ВТ_НормыСВесами.Приоритет) КАК Приоритет
        |ПОМЕСТИТЬ ВТ_МаксПриоритет
        |ИЗ
        | ВТ_НормыСВесами КАК ВТ_НормыСВесами
        |
        |СГРУППИРОВАТЬ ПО
        | ВТ_НормыСВесами.НомерСтроки
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
        | МИНИМУМ(ВТ_НормыСВесами.СуммаВесов) КАК СуммаВесов
        |ПОМЕСТИТЬ ВТ_МинСуммаВесов
        |ИЗ
        | ВТ_НормыСВесами КАК ВТ_НормыСВесами
        | ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксПриоритет КАК ВТ_МаксПриоритет
        | ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МаксПриоритет.НомерСтроки
        | И ВТ_НормыСВесами.Приоритет = ВТ_МаксПриоритет.Приоритет
        |
        |СГРУППИРОВАТЬ ПО
        | ВТ_НормыСВесами.НомерСтроки
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ВТ_НормыСВесами.НомерСтроки КАК НомерСтроки,
        | ВТ_НормыСВесами.ВидДоговора КАК ВидДоговора,
        | ВТ_НормыСВесами.Договор КАК Договор,
        | ВТ_НормыСВесами.Страна КАК Страна,
        | ВТ_НормыСВесами.Услуга КАК Услуга,
        | ВТ_НормыСВесами.Норма КАК Норма,
        | ВТ_НормыСВесами.СуммаВесов КАК СуммаВесов,
        | ВТ_НормыСВесами.Дата КАК Дата,
        | ВТ_НормыСВесами.Приоритет КАК Приоритет
        |ИЗ
        | ВТ_НормыСВесами КАК ВТ_НормыСВесами
        | ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МинСуммаВесов КАК ВТ_МинСуммаВесов
        | ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МинСуммаВесов.НомерСтроки
        | И ВТ_НормыСВесами.СуммаВесов = ВТ_МинСуммаВесов.СуммаВесов
        | ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксПриоритет КАК ВТ_МаксПриоритет
        | ПО ВТ_НормыСВесами.НомерСтроки = ВТ_МаксПриоритет.НомерСтроки
        | И ВТ_НормыСВесами.Приоритет = ВТ_МаксПриоритет.Приоритет
        |
        |УПОРЯДОЧИТЬ ПО
        | НомерСтроки»;

        Запрос.УстановитьПараметр(«Дата», КонецДня(Дата));
        Запрос.УстановитьПараметр(«ТаблицаПараметров», ТаблицаПараметров);

        Результат = Запрос.Выполнить().Выгрузить();

        Для Каждого Строка Из Результат Цикл
        стСтрокаТаблицы = Новый Структура;
        стСтрокаТаблицы.Вставить(«НомерСтроки», Строка.НомерСтроки);
        стСтрокаТаблицы.Вставить(«ВидДоговора», Строка.ВидДоговора);
        стСтрокаТаблицы.Вставить(«Договор», Строка.Договор);
        стСтрокаТаблицы.Вставить(«Страна», Строка.Страна);
        стСтрокаТаблицы.Вставить(«Услуга», Строка.Услуга);
        стСтрокаТаблицы.Вставить(«Норма», Строка.Норма);
        МассивДокумента.Добавить(стСтрокаТаблицы);
        КонецЦикла;

        Возврат МассивДокумента;
        КонецФункции оцени функцию на оптимальность

      • #36914
        bot_avaOdin — Одинэсник
        Участник

          Функция сделана по правильной схеме — вместо цикла с отдельным запросом на каждую строку передаётся таблица значений (временный набор данных) и один запрос сразу обрабатывает все параметры. Это уже большой плюс с точки зрения производительности.

          ### Плюсы функции:
          — Используется ТаблицаЗначений для передачи параметров в запрос — это оптимально.
          — Запрос построен с использованием временных таблиц (ВТ_Параметры, ВТ_ПоследнийПриказ, ВТ_НормыСВесами и т.д.) — хорошо структурирован.
          — Логика выбора последнего приказа по дате через подзапрос с МАКСИМУМом — корректна.
          — Используется взвешенный выбор нормы с приоритетами и суммами весов — это сложная бизнес-логика, реализованная в запросе.
          — Итоговый выбор норм с максимальным приоритетом и минимальной суммой весов — правильный подход для выбора «лучшей» нормы.

          ### Что можно улучшить или проверить:

          1. Индексы и объемы данных
          Убедитесь, что по таблицам и регистрам, участвующим в запросе, есть индексы по полям:
          — Документ.ПриказОНормах.Дата
          — Документ.ПриказОНормах.Нормы.Ссылка
          — Документ.ПриказОНормах.Нормы.ВидДоговора, Договор, Страна, Услуга
          Это существенно ускорит выполнение запроса.

          2. Оптимизация подзапроса с МАКСИМУМОМ
          Подзапрос для выбора последнего приказа по дате:

          — корректен, но если в базе много приказов, можно рассмотреть вариант с использованием индекса по дате и ограничением выборки.

          3. Использование ЛЕВОГО СОЕДИНЕНИЯ
          В запросе:

          — ЛЕВОЕ СОЕДИНЕНИЕ оправдано, если для некоторых параметров может не быть норм. Если всегда есть — можно заменить на ВНУТРЕННЕЕ.

          4. Обработка пустых значений
          В запросе много проверок на пустые ссылки (ПустаяСсылка). Убедитесь, что в данных нет неожиданных NULL, которые могут влиять на логику.

          5. Объем передаваемых данных
          Если мсДанные большой (тысячи строк), передача таблицы значений и выполнение запроса может быть тяжёлым. В этом случае стоит подумать о пакетной обработке.

          6. Кэширование
          Если функция вызывается часто с одними и теми же параметрами и датой, можно реализовать кэширование результатов.

          ### Итог

          Функция реализована грамотно и оптимально с точки зрения архитектуры запроса в 1С. Основные узкие места — это производительность на больших объемах данных и индексация таблиц. Если индексы настроены, а объемы не слишком велики, функция будет работать эффективно.

          Если нужна дополнительная оптимизация — стоит профилировать запрос и смотреть план выполнения, а также рассмотреть кэширование и пакетную обработку.

      Просмотр 1 ветки ответов
      • Для ответа в этой теме необходимо авторизоваться.
      База знаний 1С