При разработке конфигураций в 1С часто возникает архитектурная задача: необходимо подобрать ссылки на Документ 1 в табличную часть Документа 2 при условии, что каждая ссылка на Документ 1 может содержаться в табличной части только одного Документа 2. Разберем, как правильно реализовать такой подбор без потери производительности базы данных по мере роста количества документов.
Проанализируем базовые варианты решения данной задачи и выясним, почему некоторые популярные идеи оказываются неэффективными:
НЕ В (ВЫБРАТЬ ...) в запросе подбора: Прямая проверка присутствия ссылки через подзапрос с условием НЕ В приводит к тому, что СУБД формирует неоптимальный план запроса (оператор NOT IN). С ростом количества проведенных документов выборка начинает существенно замедляться.Наиболее производительным и простым решением является комбинация индексирования реквизита табличной части и правильного построения запроса выбора свободного Документа 1.
Разберем реализацию по шагам:
Индексировать. На уровне СУБД платформа создаст индекс, что обеспечивает поиск по полю за логарифмическое время $O(\log N)$.НЕ В используем ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью Документа 2 и проверку на ЕСТЬ NULL.Рассмотрим пример запроса для получения списка доступных к подбору документов:
ВЫБРАТЬ
ЗанятыеДокументы.Документ1 КАК Документ1
ПОМЕСТИТЬ ВТ_Занятые
ИЗ
Документ.Документ2.ТабличнаяЧасть КАК ЗанятыеДокументы
ГДЕ
ЗанятыеДокументы.Ссылка.Проведен = ИСТИНА;
ИОНДЕКСИРОВАТЬ ПО
Документ1;
ВЫБРАТЬ
Документ1Источники.Ссылка КАК Ссылка
ИЗ
Документ.Документ1 КАК Документ1Источники
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Занятые КАК ВТ_Занятые
ПО Документ1Источники.Ссылка = ВТ_Занятые.Документ1
ГДЕ
Документ1Источники.Проведен = ИСТИНА
И ВТ_Занятые.Документ1 ЕСТЬ NULL;
В данном запросе мы сначала помещаем все использованные ссылки во временную таблицу и индексируем её. Затем через ЛЕВОЕ СОЕДИНЕНИЕ оставляем только те записи Документа 1, которые ещё не фигурируют ни в одном проведенном Документе 2.
В зависимости от особенностей системы можно рассмотреть следующие подходы:
Документ1, а в качестве ресурса или реквизита — Документ2. Поскольку измерение одно, СУБД на уровне первичного ключа физически не позволит записать один и тот же Документ 1 для двух разных документов Документ 2. Запись в регистр выполняется при проведении Документа 2.Критерий отбора, указав в составе реквизит табличной части Документа 2. Запросы к составному индексу критерия отбора также выполняются быстро.Фильтрация списка подбора в форме работает на момент открытия формы и не защищает от ситуаций, когда два пользователя одновременно подбирают один и тот же Документ 1 в разные Документы 2. Для предотвращения коллизий необходимо обязательно выполнять проверку уникальности в процедуре ОбработкаПроведения Документа 2.
Разберем пример проверки перед записью связей:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Устанавливаем управляемую блокировку на проверяемые документы
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.Документ2.ТабличнаяЧасть");
ЭлементБлокировки.ИсточникЗаполнения = ТабличнаяЧасть;
ЭлементБлокировки.ИспользоватьАналог("Документ1", "Документ1");
Блокировка.Заблокировать();
// Выполняем проверку запросом на наличие дублей в других проведенных документах
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Первые 1
| ТЧ.Ссылка КАК Ссылка
|ИЗ
| Документ.Документ2.ТабличнаяЧасть КАК ТЧ
|ГДЕ
| ТЧ.Документ1 В(&МассивДокументов1)
| И ТЧ.Ссылка <> &СсылкаТекущегоДокумента
| И ТЧ.Ссылка.Проведен = ИСТИНА";
Запрос.УстановитьПараметр("МассивДокументов1", ТабличнаяЧасть.ВыгрузитьКолонку("Документ1"));
Запрос.УстановитьПараметр("СсылкаТекущегоДокумента", Ссылка);
Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
ВызватьИсключение "Один из выбранных документов уже включен в другой проведенный документ!";
КонецЕсли;
КонецПроцедуры
Подводя итог: комбинация индексирования реквизита табличной части, использование временных таблиц с левым соединением в запросах и управляемые блокировки в транзакции проведения обеспечивают максимальную скорость работы и надежность данных даже при постоянном росте базы.