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