При работе с табличными данными в 1С часто возникает необходимость обработки информации, где по определенному признаку могут встречаться дубликаты, но из этих дубликатов требуется выбрать только одну, наиболее актуальную или корректную запись. Типичный пример такой задачи – наличие нескольких записей по одному и тому же сотруднику, но с разными табельными номерами или другими изменяющимися данными. В такой ситуации возникает потребность сравнить строки и, например, выбрать ту, у которой табельный номер больше, предполагая, что более высокий табельный номер соответствует более актуальной записи.
Давайте подробно разберем, как мы можем решить эту задачу в 1С, используя различные подходы, подходящие для разных сценариев работы с данными.
Один из наиболее эффективных и гибких способов решения этой задачи, особенно если исходные данные хранятся в информационной базе (например, в регистрах сведений, справочниках или документах), – это использование запроса. Механизм запросов позволяет выполнять агрегатные функции, такие как МАКСИМУМ(), что идеально подходит для нашей задачи. Мы будем использовать вложенный запрос, чтобы сначала найти максимальный табельный номер для каждого сотрудника, а затем объединить результат с основной таблицей для получения всех необходимых полей.
Почему запрос эффективен? Запросы выполняются на стороне СУБД, что значительно ускоряет обработку больших объемов данных по сравнению с перебором в цикле на стороне 1С.
Рассмотрим структуру такого запроса по шагам:
МАКСИМУМ() к полю "ТабельныйНомер".ВНУТРЕННЕЕ СОЕДИНЕНИЕ) исходную таблицу со результатом нашего вложенного запроса. Условием соединения будет совпадение по "Сотрудник" и по "ТабельныйНомер" (который должен быть равен максимальному, найденному во вложенном запросе).Посмотрим на пример кода запроса:
ВЫБРАТЬ
Т.Сотрудник КАК Сотрудник,
Т.ТабельныйНомер КАК ТабельныйНомер,
// Здесь мы можем добавить любые другие поля из исходной таблицы,
// которые нам нужны для выбранной строки.
Т.Подразделение КАК Подразделение,
Т.Должность КАК Должность
ИЗ
ВашаТаблицаЗначений КАК Т // Предполагаем, что ВашаТаблицаЗначений - это источник данных
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ВТ.Сотрудник КАК Сотрудник,
МАКСИМУМ(ВТ.ТабельныйНомер) КАК МаксТабельныйНомер
ИЗ
ВашаТаблицаЗначений КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Сотрудник
) КАК ВложенныйЗапрос
ПО Т.Сотрудник = ВложенныйЗапрос.Сотрудник
И Т.ТабельныйНомер = ВложенныйЗапрос.МаксТабельныйНомер
В этом примере ВашаТаблицаЗначений может быть любой таблицей, доступной для запроса: это может быть виртуальная таблица регистра сведений, табличная часть документа, справочник или даже временная таблица, созданная из
ТаблицаЗначений
Выгрузить()
Загрузить()
МенеджерВременныхТаблиц
Свернуть()
Если вы работаете непосредственно с объектом
ТаблицаЗначений
Свернуть()
Разберем по шагам, как использовать
Свернуть()
Свернуть()
ТаблицуЗначений
Свернуть()
Свернуть()
Посмотрим на пример кода:
// Предположим, у нас есть ТаблицаЗначений с колонками "Сотрудник", "ТабельныйНомер" и другие.
// Например, заполним ее тестовыми данными:
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Сотрудник");
Таблица.Колонки.Добавить("ТабельныйНомер", "Число");
Таблица.Колонки.Добавить("Подразделение");
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Иванов И.И.";
НоваяСтрока.ТабельныйНомер = 100;
НоваяСтрока.Подразделение = "Отдел продаж";
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Петров П.П.";
НоваяСтрока.ТабельныйНомер = 101;
НоваяСтрока.Подразделение = "Бухгалтерия";
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Иванов И.И.";
НоваяСтрока.ТабельныйНомер = 102; // Этот табельный номер больше
НоваяСтрока.Подразделение = "Отдел логистики"; // Данные могут отличаться
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Сидоров С.С.";
НоваяСтрока.ТабельныйНомер = 103;
НоваяСтрова.Подразделение = "ИТ-отдел";
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Сотрудник = "Петров П.П.";
НоваяСтрока.ТабельныйНомер = 99; // Этот табельный номер меньше
НоваяСтрока.Подразделение = "Бухгалтерия";
// 1. Сортируем ТаблицуЗначений
// Сортировка по Сотруднику (возрастание) и ТабельномуНомеру (убывание)
// это критично для метода Свернуть(), чтобы оставалась строка с максимальным таб. номером
Таблица.Сортировать("Сотрудник ВОЗР, ТабельныйНомер УБЫВ");
// 2. Применяем метод Свернуть()
// Колонки группировки: "Сотрудник"
// Колонки суммирования: "ТабельныйНомер", "Подразделение"
// Если для агрегируемых колонок (кроме группируемых) не указать агрегатную функцию (СУММА, МАКСИМУМ и т.д.),
// то Свернуть() оставит значение из первой строки группы после сортировки.
// В нашем случае это будет строка с максимальным ТабельнымНомер.
// Если в других полях ("Подразделение", "Должность" и т.п.) данные могут отличаться
// и нам нужно сохранить данные из строки с максимальным табельным,
// то достаточно просто перечислить эти колонки как "суммируемые".
// Например: Таблица.Свернуть("Сотрудник", "ТабельныйНомер, Подразделение");
// Если же другие поля должны быть пустыми или каким-то другим образом агрегироваться, это нужно учесть.
Таблица.Свернуть("Сотрудник", "ТабельныйНомер, Подразделение");
// Теперь в Таблице остались только уникальные сотрудники с максимальными табельными номерами
// и соответствующие значения из других полей.
Для Каждого Стр Из Таблица Цикл
Сообщить("Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер + ", Подразделение: " + Стр.Подразделение);
КонецЦикла;
После выполнения этого кода в таблице
Таблица
Этот метод был предложен в форуме и является достаточно простым для понимания и реализации, особенно если таблица не очень большая. Он также хорошо работает с
ТаблицейЗначений
Разберем алгоритм:
ТаблицуЗначений
Посмотрим на пример кода:
// Предположим, ТаблицаТЗ - это наша исходная ТаблицаЗначений, уже заполненная данными.
// Для примера используем ту же тестовую Таблицу из предыдущего пункта.
// ... (код заполнения Таблицы) ...
// 1. Сортируем ТаблицуЗначений
Таблица.Сортировать("Сотрудник ВОЗР, ТабельныйНомер УБЫВ");
// Создадим новую ТаблицуЗначений для хранения результата
РезультатТЗ = Новый ТаблицаЗначений;
Для Каждого Колонка Из Таблица.Колонки Цикл
РезультатТЗ.Колонки.Добавить(Колонка.Имя, Колонка.Тип);
КонецЦикла;
// 2. Итерируем по отсортированной таблице и отбираем нужные строки
ПредСтр = Неопределено;
Для Каждого Стр Из Таблица Цикл
// Проверяем, является ли текущая строка первой для данного сотрудника
// (либо это самая первая строка в таблице вообще)
ЭтоПерваяСтрокаДляСотрудника = ?(ПредСтр = Неопределено, Истина, НЕ Стр.Сотрудник = ПредСтр.Сотрудник);
Если ЭтоПерваяСтрокаДляСотрудника Тогда
// Если это первая строка для сотрудника (после сортировки по убыванию табельного номера),
// значит, она содержит максимальный табельный номер для этого сотрудника.
// Добавляем ее в результат.
НоваяСтрокаРезультата = РезультатТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаРезультата, Стр); // Копируем все поля
КонецЕсли;
ПредСтр = Стр;
КонецЦикла;
// Теперь РезультатТЗ содержит уникальных сотрудников с максимальными табельными номерами
Для Каждого Стр Из РезультатТЗ Цикл
Сообщить("Результат (перебор): Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер);
КонецЦикла;
Этот метод более гибок, чем
Свернуть()
НайтиСтроки()
Хотя этот подход может быть менее эффективным для очень больших таблиц по сравнению с запросами или методом
Свернуть()
ТаблицыЗначений
Разберем по шагам, как это работает:
Уникальные()
НайтиСтроки()
ТаблицыЗначений
ТаблицуЗначений
Посмотрим на пример кода:
// Предположим, ТаблицаТЗ - это наша исходная ТаблицаЗначений, уже заполненная данными.
// Для примера используем ту же тестовую Таблицу из предыдущих пунктов.
// ... (код заполнения Таблицы) ...
// 1. Получаем список уникальных сотрудников из исходной таблицы
// Создаем временную таблицу из одной колонки "Сотрудник" и получаем уникальные значения
ВыборкаУникальныхСотрудников = Таблица.СкопироватьКолонки("Сотрудник");
ВыборкаУникальныхСотрудников.Свернуть("Сотрудник"); // Теперь содержит только уникальных сотрудников
// Создадим новую ТаблицуЗначений для хранения результата
РезультатТЗНайтиСтроки = Новый ТаблицаЗначений;
Для Каждого Колонка Из Таблица.Колонки Цикл
РезультатТЗНайтиСтроки.Колонки.Добавить(Колонка.Имя, Колонка.Тип);
КонецЦикла;
// 2. Итерируем по списку уникальных сотрудников
Для Каждого УникальныйСотрудник Из ВыборкаУникальныхСотрудников Цикл
// 3. Для каждого уникального сотрудника находим все его строки в исходной таблице
ПараметрыОтбора = Новый Структура("Сотрудник", УникальныйСотрудник.Сотрудник);
НайденныеСтроки = Таблица.НайтиСтроки(ПараметрыОтбора);
МаксТабельныйНомерСтрока = Неопределено;
Если НайденныеСтроки.Количество() > 0 Тогда
// 4. Определяем строку с максимальным табельным номером среди найденных
МаксТабельныйНомерСтрока = НайденныеСтроки[0]; // Инициализируем первой строкой
Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
Если СтрокаТЗ.ТабельныйНомер > МаксТабельныйНомерСтрока.ТабельныйНомер Тогда
МаксТабельныйНомерСтрока = СтрокаТЗ;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// 5. Добавляем выбранную строку в результирующую таблицу
Если МаксТабельныйНомерСтрока <> Неопределено Тогда
НоваяСтрокаРезультата = РезультатТЗНайтиСтроки.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаРезультата, МаксТабельныйНомерСтрока); // Копируем все поля
КонецЕсли;
КонецЦикла;
// Теперь РезультатТЗНайтиСтроки содержит уникальных сотрудников с максимальными табельными номерами
Для Каждого Стр Из РезультатТЗНайтиСтроки Цикл
Сообщить("Результат (НайтиСтроки): Сотрудник: " + Стр.Сотрудник + ", ТабельныйНомер: " + Стр.ТабельныйНомер);
КонецЦикла;
Выбор конкретного метода зависит от нескольких факторов:
ТаблицеЗначений
Свернуть()
Свернуть()
НайтиСтроки()
Свернуть()
Важно помнить, что в большинстве систем 1С табельный номер сотрудника предназначен быть уникальным идентификатором. Если в вашей исходной таблице встречаются несколько записей для одного и того же сотрудника с разными табельными номерами, это может указывать на потенциальные проблемы с качеством данных в источнике. Возможно, один и тот же сотрудник был ошибочно заведен несколько раз, или речь идет о сложном сценарии совместительства, где для разных должностей присваиваются разные табельные номера.
Выбор строки с максимальным табельным номером часто является логичным способом разрешения такой ситуации, предполагая, что более высокий номер относится к более актуальной или корректной записи. Однако, в некоторых случаях может потребоваться дополнительный анализ или бизнес-логика для определения "правильной" записи, помимо простого сравнения табельных номеров.
Мы рассмотрели несколько мощных и гибких способов решения задачи по выбору уникальных записей сотрудников с максимальным табельным номером в 1С. Выбирайте тот метод, который наилучшим образом соответствует вашим конкретным условиям и объему данных.