При переходе с платформы 1С 7.7 на 1С 8.3 начинающие разработчики часто сталкиваются с проблемой производительности при написании запросов. Типичная задача — найти «сиротские» записи, то есть документы одного типа, на основании которых еще не введены документы другого типа (например, найти ПоступлениеТоваровУслуг, для которых нет СчетФактураПолученный). Рассмотрим, почему стандартный подход новичков приводит к зависанию системы и как правильно реализовать эту логику в СКД и обычных запросах.
Проанализируем ситуацию, описанную в теме. Разработчик попытался выбрать документы, используя условие НЕ РАВНО (<>) в секции ГДЕ при перечислении двух таблиц через запятую. С точки зрения SQL, такой подход создает так называемое декартово произведение (Cross Join).
Рассмотрим пример кода, который вызывал проблему:
ВЫБРАТЬ
Основания.Ссылка КАК Ссылка
ИЗ
СчетФактуры КАК СчетФактуры,
Основания КАК Основания
ГДЕ
Основания.Ссылка <> СчетФактуры.ДокументОснование
Выясним причину зависания: если в таблице Основания 1000 записей, а в таблице СчетФактуры тоже 1000 записей, то при таком условии сервер базы данных попытается сравнить каждую строку из первой таблицы с каждой строкой из второй. В результате получится 1 000 000 операций сравнения. Если записей больше, количество операций растет экспоненциально, что и приводит к «циклическому» зависанию. Кроме того, логика «не равно» здесь в принципе неверна: одна и та же накладная будет «не равна» сотням других счетов-фактур, и в итоге попадет в выборку многократно, даже если у нее есть свой правильный счет-фактура.
Самый распространенный и надежный способ в 1С — это использование ЛЕВОЕ СОЕДИНЕНИЕ по ключевому полю с последующей фильтрацией отсутствующих записей. Разберем этот алгоритм по шагам:
NULL.Посмотрим на пример правильного кода:
ВЫБРАТЬ
Поступление.Ссылка КАК Ссылка
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Поступление
ЛЕВОЕ СОЕДИНЕНИЕ Документ.СчетФактураПолученный.ДокументыОснования КАК СФОснования
ПО Поступление.Ссылка = СФОснования.ДокументОснование
ГДЕ
Поступление.Дата МЕЖДУ &ДатаНач И &ДатаКон
И СФОснования.Ссылка ЕСТЬ NULL
В данном случае система найдет все поступления и попытается сопоставить их со счетами-фактурами. Если соответствие не найдено, в поле СФОснования.Ссылка подставится значение NULL, которое мы и отфильтровываем. Это работает быстро и эффективно.
Существует еще более элегантный способ, который часто работает быстрее на больших объемах данных, так как SQL-сервер может прекратить поиск сразу после нахождения первого соответствия. Проанализируем использование оператора НЕ СУЩЕСТВУЕТ (NOT EXISTS):
ВЫБРАТЬ
Поступление.Ссылка КАК Ссылка
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Поступление
ГДЕ
Поступление.Дата МЕЖДУ &ДатаНач И &ДатаКон
И НЕ СУЩЕСТВУЕТ (
ВЫБРАТЬ 1
ИЗ Документ.СчетФактураПолученный.ДокументыОснования КАК ТЧ
ГДЕ ТЧ.ДокументОснование = Поступление.Ссылка
)
Здесь мы просим систему выбрать документы только в том случае, если в табличной части счетов-фактур нет ни одной записи, указывающей на текущий документ. Это более читаемый код с точки зрения логики «выбрать то, чего нет в другом месте».
Если вы все же решили использовать временные таблицы (ПОМЕСТИТЬ), как автор темы, обязательно помните об индексации. Если временная таблица содержит тысячи строк и участвует в соединении, поиск по ней без индекса будет медленным.
Рассмотрим, как правильно дополнить запрос индексами:
ВЫБРАТЬ
СчетФактураПолученныйДокументыОснования.ДокументОснование.Ссылка КАК ДокументОснование
ПОМЕСТИТЬ ВТ_СчетФактуры
ИЗ
Документ.СчетФактураПолученный.ДокументыОснования КАК СчетФактураПолученныйДокументыОснования
ГДЕ
СчетФактураПолученныйДокументыОснования.ДокументОснование.Дата МЕЖДУ &ДатаНач И &ДатаКон
ИНДЕКСИРОВАТЬ ПО
ДокументОснование
;
Использование инструкции ИНДЕКСИРОВАТЬ ПО создает индекс в базе данных tempdb на сервере, что позволяет выполнять соединения мгновенно.
Если вы решаете эту задачу в рамках Системы компоновки данных (СКД), у вас есть два пути:
ЛЕВЫМ СОЕДИНЕНИЕМ или НЕ СУЩЕСТВУЕТ) непосредственно в текст набора данных — это самый быстрый и рекомендуемый способ.LEFT JOIN. Чтобы оставить только те записи, для которых нет соответствия, вам придется в настройках отчета добавить отбор по полю второго набора данных с условием НЕ ЗАПОЛНЕНО.Совет: При работе с составными типами данных (каким обычно является ДокументОснование) рекомендуется использовать функцию ВЫРАЗИТЬ. Это помогает системе явно понять, к какой таблице обращаться, и задействовать нужные индексы:
ВЫРАЗИТЬ(ТЧ.ДокументОснование КАК Документ.ПоступлениеТоваровУслуг)
Для решения задачи выбора отсутствующих связей в 1С 8.3 никогда не используйте условие <> в соединениях. Всегда применяйте ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на NULL или конструкцию НЕ СУЩЕСТВУЕТ. Это гарантирует стабильную работу программы даже на больших базах данных.