Как эффективно настроить подбор документов в табличную часть с ограничением повторного выбора в 1С

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

При разработке конфигураций в 1С часто возникает архитектурная задача: необходимо подобрать ссылки на Документ 1 в табличную часть Документа 2 при условии, что каждая ссылка на Документ 1 может содержаться в табличной части только одного Документа 2. Разберем, как правильно реализовать такой подбор без потери производительности базы данных по мере роста количества документов.

Анализ возможных подходов и их ограничений

Проанализируем базовые варианты решения данной задачи и выясним, почему некоторые популярные идеи оказываются неэффективными:

  1. Использование конструкции НЕ В (ВЫБРАТЬ ...) в запросе подбора: Прямая проверка присутствия ссылки через подзапрос с условием НЕ В приводит к тому, что СУБД формирует неоптимальный план запроса (оператор NOT IN). С ростом количества проведенных документов выборка начинает существенно замедляться.
  2. Использование регистра накопления: Создание регистра остатков, в который Документ 1 пишет приход, а Документ 2 — расход, является избыточным. Регистр накопления предназначен для количественного учета и автоматически поддерживает таблицы итогов. Это создает лишнюю нагрузку на СУБД, блокировки при проведении и увеличивает размер базы данных.
  3. Изменение реквизита или статуса непосредственно в Документе 1: Изменять статус Документа 1 из транзакции проведения Документа 2 некорректно. Это нарушает концепцию независимости объектов, а также вызывает ошибки при проведении, если Документ 1 находится в закрытом периоде или у пользователя нет прав на его редактирование.

Оптимальное решение: Индексация табличной части и левое соединение

Наиболее производительным и простым решением является комбинация индексирования реквизита табличной части и правильного построения запроса выбора свободного Документа 1.

Разберем реализацию по шагам:

  1. Включение индексирования: В конфигураторе для реквизита табличной части Документа 2 (в котором хранится ссылка на Документ 1) необходимо установить свойство Индексировать в значение Индексировать. На уровне СУБД платформа создаст индекс, что обеспечивает поиск по полю за логарифмическое время $O(\log N)$.
  2. Построение запроса подбора: Вместо конструкции НЕ В используем ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью Документа 2 и проверку на ЕСТЬ NULL.

Рассмотрим пример запроса для получения списка доступных к подбору документов:


ВЫБРАТЬ
	ЗанятыеДокументы.Документ1 КАК Документ1
ПОМЕСТИТЬ ВТ_Занятые
ИЗ
	Документ.Документ2.ТабличнаяЧасть КАК ЗанятыеДокументы
ГДЕ
	ЗанятыеДокументы.Ссылка.Проведен = ИСТИНА;

ИОНДЕКСИРОВАТЬ ПО
	Документ1;

ВЫБРАТЬ
	Документ1Источники.Ссылка КАК Ссылка
ИЗ
	Документ.Документ1 КАК Документ1Источники
	ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Занятые КАК ВТ_Занятые
		ПО Документ1Источники.Ссылка = ВТ_Занятые.Документ1
ГДЕ
	Документ1Источники.Проведен = ИСТИНА
	И ВТ_Занятые.Документ1 ЕСТЬ NULL;

В данном запросе мы сначала помещаем все использованные ссылки во временную таблицу и индексируем её. Затем через ЛЕВОЕ СОЕДИНЕНИЕ оставляем только те записи Документа 1, которые ещё не фигурируют ни в одном проведенном Документе 2.

Альтернативные варианты архитектуры

В зависимости от особенностей системы можно рассмотреть следующие подходы:

  1. Независимый регистр сведений: Если требуется хранить дополнительную информацию о связи или истории назначений, можно использовать независимый регистр сведений. В качестве единственного измерения задается Документ1, а в качестве ресурса или реквизита — Документ2. Поскольку измерение одно, СУБД на уровне первичного ключа физически не позволит записать один и тот же Документ 1 для двух разных документов Документ 2. Запись в регистр выполняется при проведении Документа 2.
  2. Критерии отбора: Для быстрого поиска ссылок платформенными средствами можно настроить объект Критерий отбора, указав в составе реквизит табличной части Документа 2. Запросы к составному индексу критерия отбора также выполняются быстро.

Защита от параллельного выбора (Контроль при проведении)

Фильтрация списка подбора в форме работает на момент открытия формы и не защищает от ситуаций, когда два пользователя одновременно подбирают один и тот же Документ 1 в разные Документы 2. Для предотвращения коллизий необходимо обязательно выполнять проверку уникальности в процедуре ОбработкаПроведения Документа 2.

Разберем пример проверки перед записью связей:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	
	// Устанавливаем управляемую блокировку на проверяемые документы
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Документ.Документ2.ТабличнаяЧасть");
	ЭлементБлокировки.ИсточникЗаполнения = ТабличнаяЧасть;
	ЭлементБлокировки.ИспользоватьАналог("Документ1", "Документ1");
	Блокировка.Заблокировать();
	
	// Выполняем проверку запросом на наличие дублей в других проведенных документах
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ Первые 1
		|	ТЧ.Ссылка КАК Ссылка
		|ИЗ
		|	Документ.Документ2.ТабличнаяЧасть КАК ТЧ
		|ГДЕ
		|	ТЧ.Документ1 В(&МассивДокументов1)
		|	И ТЧ.Ссылка <> &СсылкаТекущегоДокумента
		|	И ТЧ.Ссылка.Проведен = ИСТИНА";
		
	Запрос.УстановитьПараметр("МассивДокументов1", ТабличнаяЧасть.ВыгрузитьКолонку("Документ1"));
	Запрос.УстановитьПараметр("СсылкаТекущегоДокумента", Ссылка);
	
	Результат = Запрос.Выполнить();
	Если НЕ Результат.Пустой() Тогда
		ВызватьИсключение "Один из выбранных документов уже включен в другой проведенный документ!";
	КонецЕсли;

КонецПроцедуры

Подводя итог: комбинация индексирования реквизита табличной части, использование временных таблиц с левым соединением в запросах и управляемые блокировки в транзакции проведения обеспечивают максимальную скорость работы и надежность данных даже при постоянном росте базы.

← На главную