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