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