В практике программирования на платформе 1С:Предприятие часто возникают аналитические задачи, требующие не просто агрегации данных (например, получение средней суммы), но и сопоставления этих агрегатов с детальными записями. Одной из таких задач является поиск сотрудника, чья зарплата имеет минимальное отклонение от среднего значения по его подразделению. Рассмотрим подробно, как реализовать этот алгоритм с помощью языка запросов 1С.
Для решения нам необходимо выполнить несколько логических шагов. Основная сложность заключается в том, что среднее значение нельзя получить в той же строке, где находится текущая зарплата сотрудника, без использования специальных механизмов. Мы разберем два основных подхода: классический метод с временными таблицами (который работает на любых версиях 1С 8) и современный метод с оконными функциями (доступный в платформе 8.3.20 и выше).
Проанализируем ситуацию, когда нам нужно решить задачу стандартными средствами. Суть метода заключается в последовательном вычислении промежуточных данных и сохранении их во временные таблицы (ПОМЕСТИТЬ).
Шаг 1: Получение исходных данных и средних значений. Сначала мы выберем всех сотрудников и их зарплаты, а также рассчитаем среднюю зарплату в разрезе отделов.
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
Сотрудники.Отдел КАК Отдел,
Сотрудники.Зарплата КАК Зарплата
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
Справочник.Сотрудники КАК Сотрудники;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Сотрудники.Отдел КАК Отдел,
СРЕДНЕЕ(Сотрудники.Зарплата) КАК СредняяЗарплата
ПОМЕСТИТЬ ВТ_СредниеПоОтделам
ИЗ
ВТ_ИсходныеДанные КАК Сотрудники
СГРУППИРОВАТЬ ПО
Сотрудники.Отдел;
Шаг 2: Расчет отклонения (дельты). Теперь нам нужно соединить таблицу сотрудников со средними значениями и вычислить модуль разности между текущей зарплатой и средней. Для вычисления абсолютного значения в 1С используется функция ABS().
ВЫБРАТЬ
Исходные.Сотрудник КАК Сотрудник,
Исходные.Отдел КАК Отдел,
Исходные.Зарплата КАК Зарплата,
ABS(Исходные.Зарплата - Средние.СредняяЗарплата) КАК Отклонение
ПОМЕСТИТЬ ВТ_Отклонения
ИЗ
ВТ_ИсходныеДанные КАК Исходные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СредниеПоОтделам КАК Средние
ПО Исходные.Отдел = Средние.Отдел;
Шаг 3: Поиск минимального отклонения. На этом этапе мы выясним, какое именно отклонение в каждом отделе является минимальным. Это критически важно, так как мы ищем "ближайшую" зарплату.
ВЫБРАТЬ
ВТ_Отклонения.Отдел КАК Отдел,
МИНИМУМ(ВТ_Отклонения.Отклонение) КАК МинОтклонение
ПОМЕСТИТЬ ВТ_МинимальныеОтклонения
ИЗ
ВТ_Отклонения КАК ВТ_Отклонения
СГРУППИРОВАТЬ ПО
ВТ_Отклонения.Отдел;
Шаг 4: Получение финального списка. В финальном запросе мы соединяем таблицу отклонений с таблицей минимальных отклонений по двум полям: Отдел и Отклонение. Это позволит нам получить сотрудников, чье отклонение в точности равно минимальному.
ВЫБРАТЬ
ВТ_Отклонения.Отдел,
ВТ_Отклонения.Сотрудник,
ВТ_Отклонения.Зарплата
ИЗ
ВТ_Отклонения КАК ВТ_Отклонения
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МинимальныеОтклонения КАК МинОтклонения
ПО ВТ_Отклонения.Отдел = МинОтклонения.Отдел
И ВТ_Отклонения.Отклонение = МинОтклонения.МинОтклонение;
Рассмотрим более современный и эффективный способ. Если ваша база работает на актуальной версии платформы, мы можем значительно сократить текст запроса и повысить его производительность, используя оконную функцию OVER(PARTITION BY ...). Это позволяет избежать многократных соединений.
Разберем по шагам логику такого запроса:
AVG(...) OVER(PARTITION BY ...).
ВЫБРАТЬ
Т.Сотрудник,
Т.Отдел,
Т.Зарплата,
Т.Отклонение,
МИНИМУМ(Т.Отклонение) OVER(ПАРТИЦИЯ ПО Т.Отдел) КАК МинимальноеОтклонениеПоОтделу
ПОМЕСТИТЬ ВТ_Расчет
ИЗ
(ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
Сотрудники.Отдел КАК Отдел,
Сотрудники.Зарплата КАК Зарплата,
ABS(Сотрудники.Зарплата - СРЕДНЕЕ(Сотрудники.Зарплата) OVER(ПАРТИЦИЯ ПО Сотрудники.Отдел)) КАК Отклонение
ИЗ
Справочник.Сотрудники КАК Сотрудники) КАК Т;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Расчет.Сотрудник,
ВТ_Расчет.Отдел,
ВТ_Расчет.Зарплата
ИЗ
ВТ_Расчет КАК ВТ_Расчет
ГДЕ
ВТ_Расчет.Отклонение = ВТ_Расчет.МинимальноеОтклонениеПоОтделу;
При реализации подобных запросов следует учитывать несколько важных моментов, которые могут повлиять на корректность и скорость работы:
1. Индексация временных таблиц. Если в вашей организации работают тысячи сотрудников, обязательно добавляйте ИНДЕКСИРОВАТЬ ПО для полей, по которым происходит соединение (в нашем примере это Отдел). Это позволит СУБД быстрее сопоставлять данные.
2. Ситуация с равными отклонениями. Математически возможна ситуация, когда у одного сотрудника зарплата на 500 рублей больше средней, а у другого — на 500 рублей меньше. В этом случае оба сотрудника будут иметь одинаковый модуль отклонения. Наш запрос выведет обоих. Если по условиям задачи требуется строго один сотрудник, необходимо добавить дополнительную сортировку и конструкцию ВЫБРАТЬ ПЕРВЫЕ 1.
3. Использование ABS. В старых версиях платформы функция ABS() могла не поддерживаться в языке запросов. В таких случаях мы используем конструкцию ВЫБОР:
ВЫБОР
КОГДА Зарплата - СредняяЗарплата > 0
ТОГДА Зарплата - СредняяЗарплата
ИНАЧЕ СредняяЗарплата - Зарплата
КОНЕЦ КАК Отклонение
4. Оптимизация через СКД. Если данный расчет нужен для отчета, проанализируем возможность использования Системы компоновки данных. В СКД можно создать вычисляемое поле, которое будет рассчитывать среднее значение по группировке, а затем применить отбор. Это позволит избежать написания громоздкого кода в модулях.
Таким образом, мы выяснили, что наиболее надежным и универсальным способом в 1С остается использование цепочки временных таблиц с расчетом модуля разности. Это гарантирует стабильную работу на любых версиях платформы и прозрачную логику для последующей поддержки кода другими разработчиками.