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