Как в 1С 8.3 выбрать документы, которые не являются основаниями для других документов?

Программист 1С v8.3 (Управляемые формы) Бухгалтерский учет
← На главную

При переходе с платформы 1С 7.7 на 1С 8.3 начинающие разработчики часто сталкиваются с проблемой производительности при написании запросов. Типичная задача — найти «сиротские» записи, то есть документы одного типа, на основании которых еще не введены документы другого типа (например, найти ПоступлениеТоваровУслуг, для которых нет СчетФактураПолученный). Рассмотрим, почему стандартный подход новичков приводит к зависанию системы и как правильно реализовать эту логику в СКД и обычных запросах.

Разбор типичной ошибки: почему запрос «виснет»?

Проанализируем ситуацию, описанную в теме. Разработчик попытался выбрать документы, используя условие НЕ РАВНО (<>) в секции ГДЕ при перечислении двух таблиц через запятую. С точки зрения SQL, такой подход создает так называемое декартово произведение (Cross Join).

Рассмотрим пример кода, который вызывал проблему:


ВЫБРАТЬ
    Основания.Ссылка КАК Ссылка
ИЗ
    СчетФактуры КАК СчетФактуры,
    Основания КАК Основания
ГДЕ
    Основания.Ссылка <> СчетФактуры.ДокументОснование

Выясним причину зависания: если в таблице Основания 1000 записей, а в таблице СчетФактуры тоже 1000 записей, то при таком условии сервер базы данных попытается сравнить каждую строку из первой таблицы с каждой строкой из второй. В результате получится 1 000 000 операций сравнения. Если записей больше, количество операций растет экспоненциально, что и приводит к «циклическому» зависанию. Кроме того, логика «не равно» здесь в принципе неверна: одна и та же накладная будет «не равна» сотням других счетов-фактур, и в итоге попадет в выборку многократно, даже если у нее есть свой правильный счет-фактура.

Решение №1: Использование ЛЕВОГО СОЕДИНЕНИЯ (Классический метод)

Самый распространенный и надежный способ в 1С — это использование ЛЕВОЕ СОЕДИНЕНИЕ по ключевому полю с последующей фильтрацией отсутствующих записей. Разберем этот алгоритм по шагам:

  1. Выбираем все документы, которые потенциально являются основаниями (левая таблица).
  2. Присоединяем к ним таблицу подчиненных документов по ссылке (правая таблица).
  3. В условии ГДЕ указываем, что ссылка из правой таблицы должна быть равна NULL.

Посмотрим на пример правильного кода:


ВЫБРАТЬ
    Поступление.Ссылка КАК Ссылка
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Поступление
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.СчетФактураПолученный.ДокументыОснования КАК СФОснования
    ПО Поступление.Ссылка = СФОснования.ДокументОснование
ГДЕ
    Поступление.Дата МЕЖДУ &ДатаНач И &ДатаКон
    И СФОснования.Ссылка ЕСТЬ NULL

В данном случае система найдет все поступления и попытается сопоставить их со счетами-фактурами. Если соответствие не найдено, в поле СФОснования.Ссылка подставится значение NULL, которое мы и отфильтровываем. Это работает быстро и эффективно.

Решение №2: Использование конструкции ГДЕ НЕ СУЩЕСТВУЕТ (Оптимальный метод)

Существует еще более элегантный способ, который часто работает быстрее на больших объемах данных, так как SQL-сервер может прекратить поиск сразу после нахождения первого соответствия. Проанализируем использование оператора НЕ СУЩЕСТВУЕТ (NOT EXISTS):


ВЫБРАТЬ
    Поступление.Ссылка КАК Ссылка
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Поступление
ГДЕ
    Поступление.Дата МЕЖДУ &ДатаНач И &ДатаКон
    И НЕ СУЩЕСТВУЕТ (
        ВЫБРАТЬ 1
        ИЗ Документ.СчетФактураПолученный.ДокументыОснования КАК ТЧ
        ГДЕ ТЧ.ДокументОснование = Поступление.Ссылка
    )

Здесь мы просим систему выбрать документы только в том случае, если в табличной части счетов-фактур нет ни одной записи, указывающей на текущий документ. Это более читаемый код с точки зрения логики «выбрать то, чего нет в другом месте».

Важные нюансы при работе с временными таблицами

Если вы все же решили использовать временные таблицы (ПОМЕСТИТЬ), как автор темы, обязательно помните об индексации. Если временная таблица содержит тысячи строк и участвует в соединении, поиск по ней без индекса будет медленным.

Рассмотрим, как правильно дополнить запрос индексами:


ВЫБРАТЬ
    СчетФактураПолученныйДокументыОснования.ДокументОснование.Ссылка КАК ДокументОснование
ПОМЕСТИТЬ ВТ_СчетФактуры
ИЗ
    Документ.СчетФактураПолученный.ДокументыОснования КАК СчетФактураПолученныйДокументыОснования
ГДЕ
    СчетФактураПолученныйДокументыОснования.ДокументОснование.Дата МЕЖДУ &ДатаНач И &ДатаКон
ИНДЕКСИРОВАТЬ ПО
    ДокументОснование
;

Использование инструкции ИНДЕКСИРОВАТЬ ПО создает индекс в базе данных tempdb на сервере, что позволяет выполнять соединения мгновенно.

Особенности работы в СКД

Если вы решаете эту задачу в рамках Системы компоновки данных (СКД), у вас есть два пути:

  1. Запрос в наборе данных: Вы можете просто вставить один из вышеописанных запросов (с ЛЕВЫМ СОЕДИНЕНИЕМ или НЕ СУЩЕСТВУЕТ) непосредственно в текст набора данных — это самый быстрый и рекомендуемый способ.
  2. Связи наборов данных: Вы можете создать два набора данных и связать их на соответствующей вкладке. Однако помните, что связи в СКД всегда работают как LEFT JOIN. Чтобы оставить только те записи, для которых нет соответствия, вам придется в настройках отчета добавить отбор по полю второго набора данных с условием НЕ ЗАПОЛНЕНО.

Совет: При работе с составными типами данных (каким обычно является ДокументОснование) рекомендуется использовать функцию ВЫРАЗИТЬ. Это помогает системе явно понять, к какой таблице обращаться, и задействовать нужные индексы:

ВЫРАЗИТЬ(ТЧ.ДокументОснование КАК Документ.ПоступлениеТоваровУслуг)

Резюме

Для решения задачи выбора отсутствующих связей в 1С 8.3 никогда не используйте условие <> в соединениях. Всегда применяйте ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на NULL или конструкцию НЕ СУЩЕСТВУЕТ. Это гарантирует стабильную работу программы даже на больших базах данных.

← На главную