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