Как в 1С выбрать из таблицы уникальные строки сотрудников, оставив те, у которых наибольший табельный номер?

Программист 1С v8.3 (Управляемые формы)
← На главную

При работе с табличными данными в 1С часто возникает необходимость обработки информации, где по определенному признаку могут встречаться дубликаты, но из этих дубликатов требуется выбрать только одну, наиболее актуальную или корректную запись. Типичный пример такой задачи – наличие нескольких записей по одному и тому же сотруднику, но с разными табельными номерами или другими изменяющимися данными. В такой ситуации возникает потребность сравнить строки и, например, выбрать ту, у которой табельный номер больше, предполагая, что более высокий табельный номер соответствует более актуальной записи.

Давайте подробно разберем, как мы можем решить эту задачу в 1С, используя различные подходы, подходящие для разных сценариев работы с данными.

1. Использование запроса с вложенным запросом для выбора максимального табельного номера

Один из наиболее эффективных и гибких способов решения этой задачи, особенно если исходные данные хранятся в информационной базе (например, в регистрах сведений, справочниках или документах), – это использование запроса. Механизм запросов позволяет выполнять агрегатные функции, такие как МАКСИМУМ(), что идеально подходит для нашей задачи. Мы будем использовать вложенный запрос, чтобы сначала найти максимальный табельный номер для каждого сотрудника, а затем объединить результат с основной таблицей для получения всех необходимых полей.

Почему запрос эффективен? Запросы выполняются на стороне СУБД, что значительно ускоряет обработку больших объемов данных по сравнению с перебором в цикле на стороне 1С.

Рассмотрим структуру такого запроса по шагам:

  1. Вложенный запрос: Сначала нам нужно определить максимальный табельный номер для каждого сотрудника. Для этого мы сгруппируем данные по полю "Сотрудник" и применим функцию МАКСИМУМ() к полю "ТабельныйНомер".
  2. Основной запрос: Затем мы объединим (ВНУТРЕННЕЕ СОЕДИНЕНИЕ) исходную таблицу со результатом нашего вложенного запроса. Условием соединения будет совпадение по "Сотрудник" и по "ТабельныйНомер" (который должен быть равен максимальному, найденному во вложенном запросе).

Посмотрим на пример кода запроса:


ВЫБРАТЬ
    Т.Сотрудник КАК Сотрудник,
    Т.ТабельныйНомер КАК ТабельныйНомер,
    // Здесь мы можем добавить любые другие поля из исходной таблицы,
    // которые нам нужны для выбранной строки.
    Т.Подразделение КАК Подразделение,
    Т.Должность КАК Должность
ИЗ
    ВашаТаблицаЗначений КАК Т // Предполагаем, что ВашаТаблицаЗначений - это источник данных
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ
        ВТ.Сотрудник КАК Сотрудник,
        МАКСИМУМ(ВТ.ТабельныйНомер) КАК МаксТабельныйНомер
    ИЗ
        ВашаТаблицаЗначений КАК ВТ
    СГРУППИРОВАТЬ ПО
        ВТ.Сотрудник
) КАК ВложенныйЗапрос
ПО Т.Сотрудник = ВложенныйЗапрос.Сотрудник
И Т.ТабельныйНомер = ВложенныйЗапрос.МаксТабельныйНомер

В этом примере ВашаТаблицаЗначений может быть любой таблицей, доступной для запроса: это может быть виртуальная таблица регистра сведений, табличная часть документа, справочник или даже временная таблица, созданная из


ТаблицаЗначений
в конфигураторе 1С с помощью метода

Выгрузить()
или

Загрузить()
в коллекцию

МенеджерВременныхТаблиц
перед выполнением запроса.

2. Использование метода

Свернуть()
для ТаблицыЗначений

Если вы работаете непосредственно с объектом


ТаблицаЗначений
(ТЗ) в оперативной памяти 1С, и не хотите формировать запрос к базе данных, отличным инструментом для решения нашей задачи является метод

Свернуть()
. Этот метод позволяет группировать строки по заданным колонкам и выполнять агрегацию по другим колонкам. Для нашей задачи этот метод особенно эффективен, если предварительно правильно отсортировать таблицу.

Разберем по шагам, как использовать


Свернуть()
:

  1. Предварительная сортировка: Самый критичный шаг. Перед вызовом
    
    Свернуть()
    
    необходимо отсортировать вашу
    
    ТаблицуЗначений
    
    . Сортировка должна быть по полю "Сотрудник" по возрастанию и, что очень важно, по полю "ТабельныйНомер" по убыванию. Это гарантирует, что при свертке, когда 1С будет обрабатывать совпадающие строки "Сотрудника", строка с максимальным "ТабельнымНомер" будет первой в группе и именно она останется после свертки.
  2. Вызов метода
    
    Свернуть()
    
    :
    После сортировки вызываем метод
    
    Свернуть()
    
    , указывая колонки для группировки и колонки для суммирования/агрегации. В нашем случае, "Сотрудник" будет колонкой группировки, а "ТабельныйНомер" будет колонкой агрегации.

Посмотрим на пример кода:


// Предположим, у нас есть ТаблицаЗначений с колонками "Сотрудник", "ТабельныйНомер" и другие.
// Например, заполним ее тестовыми данными:
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Сотрудник");
Таблица.Колонки.Добавить("ТабельныйНомер", "Число");
Таблица.Колонки.Добавить("Подразделение");

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Иванов И.И.";
НоваяСтрока.ТабельныйНомер = 100;
НоваяСтрока.Подразделение = "Отдел продаж";

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Петров П.П.";
НоваяСтрока.ТабельныйНомер = 101;
НоваяСтрока.Подразделение = "Бухгалтерия";

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Иванов И.И.";
НоваяСтрока.ТабельныйНомер = 102; // Этот табельный номер больше
НоваяСтрока.Подразделение = "Отдел логистики"; // Данные могут отличаться

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Сидоров С.С.";
НоваяСтрока.ТабельныйНомер = 103;
НоваяСтрова.Подразделение = "ИТ-отдел";

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Петров П.П.";
НоваяСтрока.ТабельныйНомер = 99; // Этот табельный номер меньше
НоваяСтрока.Подразделение = "Бухгалтерия";


// 1. Сортируем ТаблицуЗначений
// Сортировка по Сотруднику (возрастание) и ТабельномуНомеру (убывание)
// это критично для метода Свернуть(), чтобы оставалась строка с максимальным таб. номером
Таблица.Сортировать("Сотрудник ВОЗР, ТабельныйНомер УБЫВ");

// 2. Применяем метод Свернуть()
// Колонки группировки: "Сотрудник"
// Колонки суммирования: "ТабельныйНомер", "Подразделение"
// Если для агрегируемых колонок (кроме группируемых) не указать агрегатную функцию (СУММА, МАКСИМУМ и т.д.),
// то Свернуть() оставит значение из первой строки группы после сортировки.
// В нашем случае это будет строка с максимальным ТабельнымНомер.
// Если в других полях ("Подразделение", "Должность" и т.п.) данные могут отличаться
// и нам нужно сохранить данные из строки с максимальным табельным,
// то достаточно просто перечислить эти колонки как "суммируемые".
// Например: Таблица.Свернуть("Сотрудник", "ТабельныйНомер, Подразделение");
// Если же другие поля должны быть пустыми или каким-то другим образом агрегироваться, это нужно учесть.
Таблица.Свернуть("Сотрудник", "ТабельныйНомер, Подразделение"); 

// Теперь в Таблице остались только уникальные сотрудники с максимальными табельными номерами
// и соответствующие значения из других полей.
Для Каждого Стр Из Таблица Цикл
    Сообщить("Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер + ", Подразделение: " + Стр.Подразделение);
КонецЦикла;

После выполнения этого кода в таблице


Таблица
останутся только записи для "Иванов И.И." (с табельным 102), "Петров П.П." (с табельным 101) и "Сидоров С.С." (с табельным 103).

3. Сортировка с последующей итерацией по таблице

Этот метод был предложен в форуме и является достаточно простым для понимания и реализации, особенно если таблица не очень большая. Он также хорошо работает с


ТаблицейЗначений
в памяти.

Разберем алгоритм:

  1. Сортировка: Как и в предыдущем случае, сначала необходимо отсортировать
    
    ТаблицуЗначений
    
    . Сортируем по полю "Сотрудник" (по возрастанию) и по полю "ТабельныйНомер" (по убыванию). Это позволит нам при последовательном просмотре таблицы найти все записи одного сотрудника, при этом строка с максимальным табельным номером будет идти первой в группе.
  2. Итерация и отбор: Мы будем перебирать строки отсортированной таблицы. В процессе перебора мы будем отслеживать "предыдущего" сотрудника. Когда мы встречаем нового сотрудника (или это первая строка таблицы), мы знаем, что текущая строка для этого сотрудника является строкой с максимальным табельным номером (благодаря сортировке). Эту строку мы и сохраняем в результат.

Посмотрим на пример кода:


// Предположим, ТаблицаТЗ - это наша исходная ТаблицаЗначений, уже заполненная данными.
// Для примера используем ту же тестовую Таблицу из предыдущего пункта.
// ... (код заполнения Таблицы) ...

// 1. Сортируем ТаблицуЗначений
Таблица.Сортировать("Сотрудник ВОЗР, ТабельныйНомер УБЫВ");

// Создадим новую ТаблицуЗначений для хранения результата
РезультатТЗ = Новый ТаблицаЗначений;
Для Каждого Колонка Из Таблица.Колонки Цикл
    РезультатТЗ.Колонки.Добавить(Колонка.Имя, Колонка.Тип);
КонецЦикла;

// 2. Итерируем по отсортированной таблице и отбираем нужные строки
ПредСтр = Неопределено;
Для Каждого Стр Из Таблица Цикл
    // Проверяем, является ли текущая строка первой для данного сотрудника
    // (либо это самая первая строка в таблице вообще)
    ЭтоПерваяСтрокаДляСотрудника = ?(ПредСтр = Неопределено, Истина, НЕ Стр.Сотрудник = ПредСтр.Сотрудник);
    
    Если ЭтоПерваяСтрокаДляСотрудника Тогда
        // Если это первая строка для сотрудника (после сортировки по убыванию табельного номера),
        // значит, она содержит максимальный табельный номер для этого сотрудника.
        // Добавляем ее в результат.
        НоваяСтрокаРезультата = РезультатТЗ.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрокаРезультата, Стр); // Копируем все поля
    КонецЕсли;
    
    ПредСтр = Стр;
КонецЦикла;

// Теперь РезультатТЗ содержит уникальных сотрудников с максимальными табельными номерами
Для Каждого Стр Из РезультатТЗ Цикл
    Сообщить("Результат (перебор): Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер);
КонецЦикла;

Этот метод более гибок, чем


Свернуть()
, в плане сохранения всех полей исходной строки, так как мы явно копируем строку. Однако, для очень больших таблиц он может быть медленнее, чем запрос.

4. Использование метода

НайтиСтроки()
и ручной перебор

Хотя этот подход может быть менее эффективным для очень больших таблиц по сравнению с запросами или методом


Свернуть()
, он может быть полезен в специфических сценариях, когда требуется более тонкий контроль над процессом отбора или когда данные уже находятся в памяти в формате

ТаблицыЗначений
, и количество уникальных сотрудников относительно невелико.

Разберем по шагам, как это работает:

  1. Получение списка уникальных сотрудников: Сначала нам нужно получить список всех уникальных сотрудников, присутствующих в исходной таблице. Это можно сделать, скопировав колонку "Сотрудник" и применив метод
    
    Уникальные()
    
    .
  2. Перебор уникальных сотрудников: Для каждого уникального сотрудника мы будем выполнять следующие действия.
  3. Поиск всех строк сотрудника: Используем метод
    
    НайтиСтроки()
    
    для исходной
    
    ТаблицыЗначений
    
    , чтобы найти все записи, принадлежащие текущему уникальному сотруднику.
  4. Определение максимального табельного номера: Из найденных строк для текущего сотрудника мы в цикле находим ту, у которой "ТабельныйНомер" является максимальным.
  5. Добавление в результат: Выбранную строку с максимальным табельным номером добавляем в новую результирующую
    
    ТаблицуЗначений
    
    .

Посмотрим на пример кода:


// Предположим, ТаблицаТЗ - это наша исходная ТаблицаЗначений, уже заполненная данными.
// Для примера используем ту же тестовую Таблицу из предыдущих пунктов.
// ... (код заполнения Таблицы) ...

// 1. Получаем список уникальных сотрудников из исходной таблицы
// Создаем временную таблицу из одной колонки "Сотрудник" и получаем уникальные значения
ВыборкаУникальныхСотрудников = Таблица.СкопироватьКолонки("Сотрудник");
ВыборкаУникальныхСотрудников.Свернуть("Сотрудник"); // Теперь содержит только уникальных сотрудников

// Создадим новую ТаблицуЗначений для хранения результата
РезультатТЗНайтиСтроки = Новый ТаблицаЗначений;
Для Каждого Колонка Из Таблица.Колонки Цикл
    РезультатТЗНайтиСтроки.Колонки.Добавить(Колонка.Имя, Колонка.Тип);
КонецЦикла;

// 2. Итерируем по списку уникальных сотрудников
Для Каждого УникальныйСотрудник Из ВыборкаУникальныхСотрудников Цикл
    // 3. Для каждого уникального сотрудника находим все его строки в исходной таблице
    ПараметрыОтбора = Новый Структура("Сотрудник", УникальныйСотрудник.Сотрудник);
    НайденныеСтроки = Таблица.НайтиСтроки(ПараметрыОтбора);
    
    МаксТабельныйНомерСтрока = Неопределено;
    Если НайденныеСтроки.Количество() > 0 Тогда
        // 4. Определяем строку с максимальным табельным номером среди найденных
        МаксТабельныйНомерСтрока = НайденныеСтроки[0]; // Инициализируем первой строкой
        Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
            Если СтрокаТЗ.ТабельныйНомер > МаксТабельныйНомерСтрока.ТабельныйНомер Тогда
                МаксТабельныйНомерСтрока = СтрокаТЗ;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    
    // 5. Добавляем выбранную строку в результирующую таблицу
    Если МаксТабельныйНомерСтрока <> Неопределено Тогда
        НоваяСтрокаРезультата = РезультатТЗНайтиСтроки.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрокаРезультата, МаксТабельныйНомерСтрока); // Копируем все поля
    КонецЕсли;
КонецЦикла;

// Теперь РезультатТЗНайтиСтроки содержит уникальных сотрудников с максимальными табельными номерами
Для Каждого Стр Из РезультатТЗНайтиСтроки Цикл
    Сообщить("Результат (НайтиСтроки): Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер);
КонецЦикла;

Важные соображения при выборе метода

Выбор конкретного метода зависит от нескольких факторов:

Дополнительная информация о табельных номерах в 1С

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

Выбор строки с максимальным табельным номером часто является логичным способом разрешения такой ситуации, предполагая, что более высокий номер относится к более актуальной или корректной записи. Однако, в некоторых случаях может потребоваться дополнительный анализ или бизнес-логика для определения "правильной" записи, помимо простого сравнения табельных номеров.

Мы рассмотрели несколько мощных и гибких способов решения задачи по выбору уникальных записей сотрудников с максимальным табельным номером в 1С. Выбирайте тот метод, который наилучшим образом соответствует вашим конкретным условиям и объему данных.

← На главную