В процессе поддержки и аудита информационных баз 1С мы часто сталкиваемся с аномалиями данных. Одна из таких ситуаций — наличие документа, который сформировал бухгалтерские проводки (движения по регистру бухгалтерии), но при этом его табличные части (например, "Товары" или "Услуги") абсолютно пусты. Такая ситуация может возникнуть из-за ошибок обмена, некорректного программного вмешательства или ручной корректировки движений.
Рассмотрим подробно, как с помощью языка запросов 1С выявить такие документы. Мы разберем правильную архитектуру запроса, вопросы производительности и объединение условий.
Нам необходимо выбрать документы (на примере РеализацияТоваровУслуг), которые удовлетворяют двум условиям одновременно:
Хозрасчетный).Первое, с чем нам нужно определиться — это "от чего" строить запрос. У нас есть два кандидата на роль основной таблицы:
Документ.РеализацияТоваровУслуг).Чтобы сразу отсечь документы, которые не проведены или не имеют проводок, логичнее и правильнее выбирать данные из регистра бухгалтерии. Однако здесь есть важный нюанс, касающийся производительности.
Важно: Не используйте виртуальные таблицы (например, РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто), если вам нужно просто проверить наличие записей. Обращение к виртуальной таблице заставляет платформу выполнять сложные соединения с таблицами значений субконто, что значительно замедляет выполнение запроса на больших объемах данных.
Для нашей задачи идеально подходит основная (физическая) таблица регистра:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Хозрасчетный.Регистратор
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
Использование ключевого слова РАЗЛИЧНЫЕ обязательно, так как один документ может создавать десятки проводок, а нам в выборке документ нужен только один раз.
Теперь разберем, как определить, что у документа пустая табличная часть. В языке запросов 1С для поиска отсутствующих данных используется классический прием: Левое соединение с последующей проверкой на NULL.
Суть метода:
ЛЕВОЕ СОЕДИНЕНИЕ.NULL.ГДЕ мы оставляем только те строки, где есть NULL.Соединим полученные знания. Напишем запрос, который ищет документы, существующие в регистре бухгалтерии, но не имеющие строк в ТЧ "Товары".
ВЫБРАТЬ РАЗЛИЧНЫЕ
Хозрасчетный.Регистратор КАК СсылкаНаДокумент
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
// Присоединяем табличную часть документа
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК РТУ_Товары
ПО Хозрасчетный.Регистратор = РТУ_Товары.Ссылка
ГДЕ
// Фильтруем только по нужному типу документа, так как в регистре могут быть разные документы
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
// Главное условие: запись в ТЧ не найдена
И РТУ_Товары.Ссылка ЕСТЬ NULL
Документы часто имеют несколько табличных частей. Например, в "Реализации товаров и услуг" есть и Товары, и Услуги. Если мы используем запрос выше, мы можем ошибочно отобрать документ, в котором продали только услугу (ТЧ "Товары" пуста, но документ корректный).
Чтобы найти документы, которые полностью пусты (нет ни товаров, ни услуг), нам нужно сделать левое соединение ко всем значимым таблицам и проверить их все.
Посмотрим на полный код решения:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Хозрасчетный.Регистратор КАК ДокументОшибки
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
// Соединяем с ТЧ Товары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК ТЧ_Товары
ПО Хозрасчетный.Регистратор = ТЧ_Товары.Ссылка
// Соединяем с ТЧ Услуги
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Услуги КАК ТЧ_Услуги
ПО Хозрасчетный.Регистратор = ТЧ_Услуги.Ссылка
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
// Проверяем, что пусто ВЕЗДЕ
И ТЧ_Товары.Ссылка ЕСТЬ NULL
И ТЧ_Услуги.Ссылка ЕСТЬ NULL
Обратите внимание на использование логического оператора И в условиях. Документ попадет в выборку только если обе табличные части вернули NULL.
Иногда запрос через соединения может быть громоздким для восприятия. Рассмотрим альтернативный подход, который семантически читается как "Выбери документы, которых нет в списке документов с товарами".
Этот подход также может быть полезен, если вы сначала отбираете документы по регистру, а потом фильтруете их:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Хозрасчетный.Регистратор
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
И НЕ Хозрасчетный.Регистратор В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
Т.Ссылка
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Т)
И НЕ Хозрасчетный.Регистратор В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
У.Ссылка
ИЗ
Документ.РеализацияТоваровУслуг.Услуги КАК У)
Примечание по производительности: Современный планировщик SQL-запросов (Query Optimizer) в большинстве случаев приведет оба варианта (JOIN и IN/EXISTS) к схожему плану выполнения. Однако вариант с LEFT JOIN ... IS NULL традиционно считается более предсказуемым и стабильным стандартом в разработке на 1С.
На практике полезно не только найти ошибку, но и понять её причину. Если ваш запрос вернул данные, проверьте следующие гипотезы:
При запуске таких запросов на реальных базах данных обязательно добавляйте отбор по Периоду в таблицу регистра бухгалтерии (Хозрасчетный.Период МЕЖДУ ...). Сканирование всей таблицы регистра за все годы существования базы может вызвать существенную нагрузку на сервер СУБД.