При работе в среде 1С:Предприятие 7.7 программисты часто сталкиваются с жестким ограничением встроенного языка запросов (так называемого «черного запроса»). Основная проблема заключается в том, что механизм запросов к регистрам спроектирован для работы исключительно с измерениями, ресурсами и реквизитами самого регистра, а также с реквизитами шапки документов. В этой статье мы подробно разберем, почему стандартный запрос не видит цену в строке, и рассмотрим эффективные методы решения этой задачи.
Для начала проанализируем ситуацию. В 1С 7.7 регистры и документы — это разные физические таблицы в базе данных. Когда мы пишем конструкцию вида Регистр.ОстаткиТоваров.ТекущийДокумент, запрос обращается к ссылке на документ. Мы можем «дотянуться» до даты документа или любого реквизита шапки (например, Склад или Контрагент), так как они хранятся в одной строке с заголовком документа. Однако цена товара хранится в табличной части (строках), а связи «один к одному» между записью регистра и конкретной строкой документа в стандартной конфигурации не существует.
Рассмотрим стандартный запрос, который вызывает затруднения:
//{{ЗАПРОС(ПоследниеПоступления)
|Период с НачДок по ДатаКон;
|Товар = Регистр.ОстаткиТоваров.Товар;
|Док = Регистр.ОстаткиТоваров.ТекущийДокумент;
|ОстатокТовара = Регистр.ОстаткиТоваров.ОстатокТовара;
// Попытка обратиться к цене из ТЧ здесь не сработает
|Функция ПриходКол = Приход(ОстатокТовара);
|Группировка Товар без групп;
|Группировка Док;
//}}ЗАПРОС
В данном случае переменная Док после выполнения запроса будет содержать объект типа «Документ». Чтобы получить цену, нам пришлось бы внутри цикла обхода результатов запроса открывать каждый документ, искать в нем нужную строку с товаром и извлекать значение. Рассмотрим, как сделать это оптимальнее.
Если количество документов в отчете не исчисляется тысячами, можно воспользоваться методом постобработки. Однако важно делать это правильно, чтобы не «повесить» систему постоянным обращением к диску. Вместо медленного перебора строк документа через ВыбратьСтроки(), мы применим метод ВыгрузитьТабличнуюЧасть().
Разберем алгоритм действий:
ТаблицуЗначений.Пример кода для реализации этого подхода:
Запрос = СоздатьОбъект("Запрос");
// ... Текст запроса ...
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка("Товар") = 1 Цикл
Пока Запрос.Группировка("Док") = 1 Цикл
ТекДок = Запрос.Док;
ТЗ_ТЧ = СоздатьОбъект("ТаблицаЗначений");
ТекДок.ВыгрузитьТабличнуюЧасть(ТЗ_ТЧ);
НомСтроки = 0;
Если ТЗ_ТЧ.НайтиЗначение(Запрос.Товар, НомСтроки, "Товар") = 1 Тогда
ЦенаИзТЧ = ТЗ_ТЧ.ПолучитьЗначение(НомСтроки, "Цена");
// Теперь у нас есть цена для вывода в отчет
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЕсли;
Важный совет: Чтобы ускорить процесс, можно использовать кэширование. Если один и тот же документ встречается для разных товаров, сохраняйте его ТаблицуЗначений в СписокЗначений или индексированную таблицу (с помощью внешних компонент), чтобы не выгружать ТЧ повторно.
Для высоконагруженных систем это единственный «правильный» путь. Использование внешней компоненты 1С++ позволяет написать прямой SQL-запрос к таблицам базы данных. В SQL мы можем легко объединить (JOIN) таблицу движений регистра (например, RAXXXX) с таблицей строк документа (DTXXXX).
Проанализируем преимущества этого метода:
Схематично SQL-запрос будет выглядеть так (где $НомерСтроки — это реквизит, связывающий регистр со строкой, если он добавлен):
SELECT
Reg.Товар as [Товар $Справочник.Товары],
DocRows.Цена as Цена
FROM
[Регистр.ОстаткиТоваров] as Reg
INNER JOIN
[ДокументСтроки.Приходная] as DocRows ON Reg.ТекущийДокумент = DocRows.IDDoc
WHERE
Reg.Товар = DocRows.Товар
Выясним причину, по которой цена отсутствует в регистре. В классической архитектуре 1С 7.7 регистр остатков хранит только количество. Если вам постоянно требуется цена в отчетах по остаткам, самым разумным (хоть и трудозатратным) решением будет добавление реквизита Цена или ресурса Сумма непосредственно в сам регистр.
Рассмотрим шаги по реализации без риска разрушения последовательности:
ОстаткиТоваров новый реквизит Цена.Записать() в режиме редактирования записей регистра без выполнения Провести(), чтобы не затронуть последовательность и другие регистры.После этого ваш запрос станет максимально простым и быстрым:
|Товар = Регистр.ОстаткиТоваров.Товар;
|Цена = Регистр.ОстаткиТоваров.Цена; // Теперь это поле доступно напрямую!
|Функция ПриходКол = Приход(ОстатокТовара);
Если в вашей конфигурации сохранились другие регистры (например, Продажи или ПартииТоваров), проверьте, нет ли там суммовых показателей. В 1С 7.7 цена часто вычисляется как РесурсСумма / РесурсКоличество. Если регистр партий ведется, то цена поступления всегда доступна в нем. Достаточно изменить объект запроса с ОстаткиТоваров на ПартииТоваров, и вы получите доступ к стоимостным показателям без обращения к табличным частям документов.
Мы проанализировали ситуацию и выяснили, что стандартный запрос 1С 7.7 ограничен в работе с табличными частями. Выбор решения зависит от ваших технических возможностей: для разовых отчетов подойдет выгрузка ТЧ в таблицу значений, для производительных систем — прямые SQL-запросы, а для долгосрочного удобства — корректировка структуры регистров. Помните, что архитектура «Торговли» 2002 года выпуска часто требует оптимизации под современные объемы данных.