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