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