В процессе разработки на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с задачей получения данных из последнего хронологического документа. Типичный пример — необходимость узнать цену номенклатуры из самой свежей реализации конкретному контрагенту с определенного склада. Стандартный подход через создание «Черного запроса» (встроенного конструктора запросов) в этой ситуации часто оказывается крайне неэффективным и медленным. Разберем подробно, почему так происходит и какие альтернативные методы позволяют решить задачу мгновенно.
Рассмотрим ситуацию, описанную в исходном сообщении. Автор пытается использовать штатный механизм запроса к регистру ПартииТоваров. Несмотря на то что в группировке указан обратный порядок, отчет выполняется очень долго. Проанализируем основные причины низкой производительности такого кода:
Регистр.ПартииТоваров.ТекущийДокумент.РасходнаяНакладная.Склад. Для каждой строки регистра система вынуждена обращаться к таблице заголовков документов, чтобы получить значение склада. В SQL-версии это порождает сложные соединения таблиц, а в DBF-версии — колоссальное количество операций чтения, что критически замедляет процесс.Условие), система не всегда может эффективно задействовать индексы таблиц базы данных, особенно если фильтрация идет по реквизитам, а не по измерениям регистра.Выясним, какие существуют способы оптимизации, чтобы получение цены занимало доли секунды вместо минут ожидания.
Это самый эффективный «штатный» способ для платформы 7.7. Вместо того чтобы заставлять систему строить сложный запрос и сортировать его результат, мы можем напрямую обратиться к таблице движений регистра, используя встроенные индексы. Проанализируем ситуацию: если нам нужна последняя продажа, логично начать чтение таблицы с конца.
Для этого воспользуемся объектом типа Регистр и методом ВыбратьДвижения(). Рассмотрим по шагам, как реализовать этот алгоритм:
// Создаем объект регистра
Рег = СоздатьОбъект("Регистр.ПартииТоваров");
// Если Склад, Товар и Контрагент являются измерениями, устанавливаем фильтр
// Это позволит системе использовать индекс и работать максимально быстро
Рег.УстановитьФильтр(_Товар, ВыбСклад, Контрагент);
// Ключевой момент: передаем параметры в ВыбратьДвижения
// Первый параметр - ДатаНачала (пустая или начало времен)
// Второй параметр - ДатаКонца (рабочая дата или дата документа)
// Третий параметр - Флаг выборки (необязательно)
// Четвертый параметр - 1 для обратного порядка
Если Рег.ВыбратьДвижения(,, 1) = 1 Тогда
Пока Рег.ПолучитьДвижение() = 1 Цикл
// Так как порядок обратный, первое же полученное движение будет последним хронологически
ПоследняяЦена = Рег.Цена; // Предположим, цена есть в ресурсах или реквизитах
ДокПродажи = Рег.ТекущийДокумент();
// Прерываем цикл, так как цель достигнута
Прервать;
КонецЦикла;
КонецЕсли;
Важное примечание: Метод ВыбратьДвижения с параметром ОбратныйПорядок(1) заставляет движок базы данных начать чтение индекса с максимального значения. Это позволяет найти нужную запись почти мгновенно, не просматривая всю историю за предыдущие годы.
Если задача получения последней цены стоит очень часто (например, при подборе товара в каждый документ), стоит рассмотреть вариант изменения архитектуры. Рассмотрим подробнее предложение использовать периодические реквизиты справочника Номенклатура.
В 1С 7.7 можно создать периодический реквизит, например, ЦенаПоследнейРеализации. При проведении документа продажи программно записывается новое значение этого реквизита. Плюсы этого метода:
Получить(Дата) работает очень быстро.Однако проанализируем и минусы: при «отмене проведения» или изменении старых документов поддерживать актуальность таких данных крайне сложно. Придется писать сложную логику пересчета всей цепочки цен при удалении документа из середины периода. Поэтому данный метод хорош только для справочной информации, не влияющей на финансовый результат напрямую.
Для высоконагруженных систем на базе MS SQL Server лучшим решением будет использование внешней компоненты 1С++. Она позволяет миновать объектную модель 1С и обратиться к данным напрямую через SQL. Посмотрим на пример логики такого запроса:
Проанализируем SQL-конструкцию:
SELECT TOP 1
row_id,
$Движения.Цена as Цена
FROM
$Регистр.ПартииТоваров as Движения
WHERE
$Движения.Товар = :ВыбТовар
AND $Движения.Склад = :ВыбСклад
ORDER BY
DATE DESC, TIME DESC, IDDOC DESC
Такой запрос на уровне сервера базы данных отработает за миллисекунды, так как SELECT TOP 1 в сочетании с ORDER BY ... DESC по индексированным полям даты и времени — это самая оптимизированная операция для SQL.
Разберем, какой метод лучше подходит под конкретные условия:
ВыбратьДвижения с параметром ОбратныйПорядок(1). Это стандартное средство, не требующее внешних библиотек.Стоит также обратить внимание на важный нюанс, упомянутый в сообщениях форума. Иногда разработчики пытаются решить проблему через выгрузку результата запроса в ТаблицуЗначений (ТЗ) и последующую сортировку ТЗ. Посмотрим на этот сценарий:
Выгрузка в ТЗ не ускоряет сам процесс получения данных. Если запрос выполняется 2 минуты, то выгрузка его в таблицу займет те же 2 минуты. Сортировка таблицы поможет вам удобно обойти данные, но не решит проблему «медленного отчета».
Также важно помнить, что в 1С 7.7 ручное управление порядком группировок в запросе (например, Упорядочить по Док.ДатаДок) влияет только на то, как данные будут представлены в объекте Запрос после его выполнения. Это не меняет физический способ выборки данных из файлов БД.
Подведем итоги: Для быстрого поиска последней цены в 1С 7.7 откажитесь от использования стандартного «Черного запроса» в пользу метода ВыбратьДвижения(,, 1). Это позволит системе использовать индексы и мгновенно переходить к самым свежим записям, экономя время пользователей и ресурсы сервера.