В практике разработчика 1С часто возникает задача контроля заполнения данных. Одна из самых распространенных ситуаций: необходимо вывести список всех физических лиц и для каждого из них показать те виды документов (например, Паспорт, ИНН, СНИЛС), которые еще не занесены в систему. Это классическая задача на вычитание множеств. В языке SQL для этого существует оператор EXCEPT, однако в платформе 1С он не реализован, поэтому нам приходится использовать обходные пути.
В данной статье мы подробно разберем, как правильно составить запрос, чтобы получить «матрицу» недостающих данных, проанализируем производительность различных подходов и рассмотрим лучшие практики оптимизации.
Для того чтобы понять, каких данных не хватает, мы сначала должны сформировать полную картину того, что должно быть. Нам нужно получить таблицу, где каждое физическое лицо сопоставлено с каждым возможным видом документа. В математике это называется декартовым произведением.
Разберем этот метод по шагам:
ИЗ.ГДЕ ... ЕСТЬ NULL по полю из регистра. Если записи в регистре нет, поле будет пустым, и эта строка попадет в результат.Рассмотрим пример кода:
ВЫБРАТЬ
ПолнаяСетка.ФизЛицо КАК ФизЛицо,
ПолнаяСетка.ВидДокумента КАК ВидДокумента
ИЗ
(ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК ФизЛицо,
ВидыДокументов.Ссылка КАК ВидДокумента
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица,
Справочник.ВидыДокументовФизическихЛиц КАК ВидыДокументов
ГДЕ
НЕ ФизическиеЛица.ПометкаУдаления
И НЕ ВидыДокументов.ПометкаУдаления) КАК ПолнаяСетка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДокументыФизическихЛиц.СрезПоследних КАК ДокументыРегистр
ПО ПолнаяСетка.ФизЛицо = ДокументыРегистр.Физлицо
И ПолнаяСетка.ВидДокумента = ДокументыРегистр.ВидДокумента
ГДЕ
ДокументыРегистр.ВидДокумента ЕСТЬ NULL
Важный нюанс: Обратите внимание, что соединение идет по двум полям одновременно. Это критически важно, иначе логика поиска «пустот» нарушится. Если у сотрудника Иванова есть Паспорт, но нет ИНН, то при соединении только по ФизЛицо запись не будет считаться пустой. Нам же нужно проверить наличие конкретной пары.
Существует альтернативный синтаксис, который может показаться более читаемым. Платформа 1С позволяет использовать конструкцию В для сравнения сразу нескольких полей (кортежей). Мы можем выбрать все возможные комбинации и исключить те, которые уже существуют в базе.
Проанализируем этот вариант:
ВЫБРАТЬ
т1.Ссылка КАК ФизЛицо,
т2.Ссылка КАК ВидДокумента
ИЗ
Справочник.ФизическиеЛица КАК т1,
Справочник.ВидыДокументовФизическихЛиц КАК т2
ГДЕ
НЕ (т1.Ссылка, т2.Ссылка) В
(ВЫБРАТЬ
т3.ФизЛицо,
т3.ВидДокумента
ИЗ
РегистрСведений.ДокументыФизическихЛиц.СрезПоследних КАК т3)
Несмотря на лаконичность, у этого метода есть свои особенности. В зависимости от версии СУБД (MS SQL, PostgreSQL) и версии платформы 1С, такой запрос может работать медленнее, чем ЛЕВОЕ СОЕДИНЕНИЕ. SQL-серверу проще оптимизировать соединение (используя Anti Semi Join), чем вложенный фильтр по кортежу. Однако на небольших объемах данных (до нескольких тысяч сотрудников) разница будет незаметна.
Если в вашей базе данных десятки тысяч сотрудников, прямое декартово перемножение справочников может привести к серьезным нагрузкам на сервер. Представьте: 20 000 сотрудников умножаем на 10 видов документов — получаем промежуточную таблицу в 200 000 строк в памяти. Чтобы ускорить процесс, проанализируем ситуацию и применим временные таблицы.
Рассмотрим правильный алгоритм оптимизации:
ВТ_ФизЛица.ВТ_ВидыДокументов.ВТ_ДанныеРегистра и обязательно проиндексируем её по полям ФизЛицо и ВидДокумента.Посмотрим на пример оптимизированного запроса:
// Шаг 1: Готовим список сотрудников
ВЫБРАТЬ Ссылка КАК ФизЛицо ПОМЕСТИТЬ ВТ_Сотрудники ИЗ Справочник.ФизическиеЛица ГДЕ НЕ ПометкаУдаления;
// Шаг 2: Готовим список видов документов
ВЫБРАТЬ Ссылка КАК ВидДокумента ПОМЕСТИТЬ ВТ_Виды ИЗ Справочник.ВидыДокументовФизическихЛиц ГДЕ НЕ ПометкаУдаления;
// Шаг 3: Получаем срез последних в ИНДЕКСИРОВАННУЮ временную таблицу
ВЫБРАТЬ ФизЛицо, ВидДокумента ПОМЕСТИТЬ ВТ_СуществующиеДокументы
ИЗ РегистрСведений.ДокументыФизическихЛиц.СрезПоследних
ИНДЕКСИРОВАТЬ ПО ФизЛицо, ВидДокумента;
// Шаг 4: Финальное объединение
ВЫБРАТЬ
Сотр.ФизЛицо,
Виды.ВидДокумента
ИЗ
ВТ_Сотрудники КАК Сотр,
ВТ_Виды КАК Виды
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СуществующиеДокументы КАК Сущ
ПО Сотр.ФизЛицо = Сущ.ФизЛицо И Виды.ВидДокумента = Сущ.ВидДокумента
ГДЕ
Сущ.ФизЛицо ЕСТЬ NULL;
Почему это работает быстрее? Платформа 1С при обращении к виртуальной таблице СрезПоследних внутри сложного соединения не всегда может эффективно построить план запроса на уровне SQL. Вынос среза во временную таблицу с явным индексированием гарантирует максимально быструю работу соединения.
При решении подобных задач часто допускаются ошибки, которые приводят к неверным результатам или «тормозам» системы. Проанализируем основные моменты:
ГДЕ НЕ ПометкаУдаления, в отчет попадут архивные виды документов или уволенные сотрудники, что создаст лишний «шум».NULL убедитесь, что в ваших исходных справочниках нет битых данных.СрезаПоследних убедитесь, что в конфигураторе у регистра сведений измерения ФизЛицо и ВидДокумента стоят первыми. Это позволит SQL-серверу использовать индекс регистра наиболее эффективно.Выяснив причину сложности задачи (отсутствие прямого оператора вычитания), мы убедились, что комбинация декартова произведения и левого соединения является самым надежным и универсальным инструментом в арсенале разработчика 1С.