При разработке сложных учетных систем в среде 1С:Предприятие часто возникает задача однозначной идентификации конкретной строки табличной части документа или справочника. Например, это требуется при интеграции с внешними информационными системами, при связывании нескольких табличных частей между собой (когда строки одной таблицы детализируют строки другой), или при точечном отражении движений по регистрам — здесь поможет редактирование записей регистров с контролем идентификаторов.
Стандартный реквизит НомерСтроки для этой роли категорически не подходит. При удалении строк, изменении их порядка или ручной сортировке индексы сдвигаются. Это неизбежно приводит к рассинхронизации связанных данных — здесь пригодится обмен данными 1С через COM и веб-сервисы. В этой статье мы подробно разберем, как решить задачу присвоения строке действительно уникального и стабильного идентификатора, рассмотрим особенности хранения данных в СУБД, а также изучим лучшие практики и готовые стандарты 1С.
Прежде чем переходить к написанию кода, проанализируем, как платформа 1С:Предприятие работает с табличными частями на физическом уровне базы данных. Строка табличной части не является самостоятельным ссылочным объектом (в отличие от элементов справочников или документов).
В СУБД (MS SQL, PostgreSQL) для каждой табличной части создается отдельная физическая таблица — для этого подойдёт инструмент для анализа структуры таблиц СУБД. Первичным ключом (индексом) в ней выступает составное поле:
Когда пользователь меняет строки местами или удаляет строку из середины списка, платформа перезаписывает всю таблицу ТЧ для данного документа, обновляя поле НомерСтроки. Именно поэтому использовать номер строки для связи с внешними таблицами или другими ТЧ нельзя. Нам необходим выделенный реквизит, который будет хранить постоянный идентификатор на протяжении всей «жизни» строки.
Самый распространенный и надежный способ уникальной идентификации строки — использование глобального уникального идентификатора (GUID). Платформа 1С имеет встроенный тип данных УникальныйИдентификатор. Рассмотрим по шагам, как реализовать этот механизм.
Шаг 1. Настройка метаданных в Конфигураторе
Добавим в нужную табличную часть новый реквизит. Назовем его, например, ИдентификаторСтроки. При выборе типа данных крайне важно указать именно системный тип УникальныйИдентификатор, а не Строка(36).
Использование встроенного типа УникальныйИдентификатор имеет ключевые преимущества для производительности СУБД:
uniqueidentifier в MS SQL или binary(16)), что занимает в разы меньше места, чем текст.JOIN) по бинарному полю выполняются значительно быстрее, чем по строковому.Шаг 2. Генерация идентификатора при добавлении новой строки
Выясним, в какой момент генерировать идентификатор. Лучше всего делать это интерактивно при добавлении строки пользователем на форме. Для этого разберем пример обработки события табличной части ПриНачалеРедактирования в модуле управляемой формы:
&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
Если НоваяСтрока Тогда
// Если строка копируется, у нее уже может быть заполнен старый ID.
// Нам нужно в любом случае гарантировать уникальность новой строки.
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
ТекущиеДанные.ИдентификаторСтроки = Новый УникальныйИдентификатор;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Шаг 3. Инициализация идентификаторов для существующих документов
Если в вашей базе данных уже есть заполненные документы, у существующих строк поле ИдентификаторСтроки окажется пустым. Нам потребуется разовая обработка, которая обойдет документы и заполнит пустые значения. Посмотрим на пример кода, который решает эту задачу на сервере:
&НаСервере
Процедура ЗаполнитьИдентификаторыСуществующихСтрок()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| РеализацияТоваровУслугТовары.ИдентификаторСтроки = ЗНАЧЕНИЕ(УникальныйИдентификатор.ПустаяСсылка)";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Для Каждого СтрокаТЧ Из ДокументОбъект.Товары Цикл
Если Не ЗначениеЗаполнено(СтрокаТЧ.ИдентификаторСтроки) Тогда
СтрокаТЧ.ИдентификаторСтроки = Новый УникальныйИдентификатор;
КонецЕсли;
КонецЦикла;
// Отключаем проверки при записи, если это допустимо, для ускорения процесса
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Записать();
КонецЦикла;
КонецПроцедуры
Использование тяжелых 128-битных GUID оправдано не всегда. Если перед вами стоит локальная задача — например, связать на форме документа две табличные части (например, основную таблицу «Товары» и подчиненную таблицу «СерийныеНомера»), то использовать GUID избыточно — для этого подойдёт автоматизация связи строк и таблиц в 1С.
Для таких целей гораздо оптимальнее использовать простой числовой Ключ связи. Рассмотрим, как это реализовать:
КлючСвязи с типом Число(10, 0).КлючСвязи, что и у текущей строки основной таблицы.Посмотрим на пример функции генерации числового ключа связи при добавлении новой строки в основную ТЧ:
&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
Если НоваяСтрока Тогда
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
ТекущиеДанные.КлючСвязи = ПолучитьМаксимальныйКлючСвязи() + 1;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ПолучитьМаксимальныйКлючСвязи()
МаксКлюч = 0;
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
Если СтрокаТЧ.КлючСвязи > МаксКлюч Тогда
МаксКлюч = СтрокаТЧ.КлючСвязи;
КонецЕсли;
КонецЦикла;
Возврат МаксКлюч;
КонецФункции
При выборе строки в основной таблице на форме мы просто устанавливаем отбор по полю КлючСвязи для подчиненной таблицы. Это решение работает молниеносно, занимает минимум места в базе и легко читается в запросах.
В некоторых обсуждениях на форумах предлагают радикальный вариант — создать отдельный справочник, элементы которого будут выступать в роли строк. В табличную часть при этом добавляется реквизит типа СправочникСсылка.ИмяСправочника.
Рассмотрим плюсы и минусы такого подхода:
Поэтому к использованию справочников стоит прибегать только тогда, когда строка ТЧ превращается в самостоятельную бизнес-сущность, требующую ведения собственной истории или дополнительных реквизитов.
Если вы работаете в современной конфигурации, разработанной на базе БСП (Библиотеки стандартных подсистем), обратите внимание на то, как эта задача уже решена вендором. В документах, где требуется точечное сопоставление (например, в заказах, обеспечивающих резервы, или в транспортных накладных), разработчики 1С используют связку двух реквизитов в ТЧ:
ИдентификаторСтроки — тип УникальныйИдентификатор.КодСтроки — тип Число.В типовых решениях также встроены готовые программные механизмы. Например, программное заполнение уникальных идентификаторов строк при формировании документов кодом выполняется вызовом экспортных методов из общего модуля:
// Пример использования готового общего модуля из ERP/УТ
ОбщегоНазначенияУТ.ДобавитьИдентификаторСтрокВТаблицу(Объект.Товары);
Перед тем как писать свои алгоритмы генерации на проектах с типовыми конфигурациями, обязательно изучите состав общих модулей. Скорее всего, нужный вам велосипед уже спроектирован и протестирован разработчиками 1С.
Рассмотрим частую ошибку начинающих разработчиков. Платформа 1С в управляемом интерфейсе предоставляет механизм получения идентификатора строки через коллекцию ДанныеФормыКоллекция.
Метод Элементы.ИмяТаблицы.ТекущаяСтрока возвращает числовой идентификатор. Его также можно получить с помощью метода ПолучитьИдентификатор() на клиенте. Однако помните о важном ограничении:
Этот идентификатор живет только в рамках текущего сеанса работы формы!
Как только форма закрывается, или происходит серверный вызов с пересозданием контекста формы, эти идентификаторы сбрасываются и генерируются заново. Использовать их для хранения в базе данных или для связи данных между сеансами категорически запрещено.
Проанализируем еще одну полезную возможность, появившуюся в современных версиях платформы (начиная с 8.3.22). Ранее для получения строкового представления GUID ссылки в запросах приходилось выгружать результат в таблицу значений и обрабатывать его кодом.
Теперь в языке запросов доступна встроенная функция УникальныйИдентификатор(). Выглядит это следующим образом:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК ДокументСсылка,
УникальныйИдентификатор(РеализацияТоваровУслугТовары.ИдентификаторСтроки) КАК СтрокаGUID
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
Это нововведение значительно упрощает построение интеграционных пакетов данных, позволяя формировать готовые текстовые GUID прямо на уровне СУБД в один шаг — поможет универсальная выгрузка документов в XML и JSON.
Мы детально разобрали варианты уникальной идентификации строк табличной части в 1С. Подведем итоги:
УникальныйИдентификатор.Строка(36) вместо системного типа УникальныйИдентификатор, чтобы не снижать производительность СУБД.