В разработке на платформе 1С:Предприятие 7.7 часто возникает задача получения самого последнего документа, совершившего движение по конкретному счету с учетом определенных субконто. Стандартный объект БухгалтерскиеИтоги при использовании метода ВыбратьПериоды всегда предоставляет данные в хронологическом порядке (от старых к новым). Если база данных содержит информацию за несколько лет, простой перебор всех движений для поиска последнего становится крайне неэффективным и медленным процессом.
В данной статье мы разберем несколько эффективных способов решения этой задачи: от штатных механизмов встроенного языка до использования прямых запросов к базе данных SQL.
Рассмотрим, пожалуй, самый изящный штатный способ, о котором часто забывают разработчики, пытаясь решить всё через БухгалтерскиеИтоги. Объект Операция обладает методом ВыбратьОперацииСПроводками, который позволяет не только накладывать фильтр по счету, но и задавать порядок выборки.
Проанализируем пример кода, который позволяет быстро спозиционироваться на последнем движении:
// Создаем объект Операция
Опер = СоздатьОбъект("Операция");
// Параметры метода:
// 1, 2 - Дата начала и конца периода
// 3 - Выбранный счет
// 4 - План счетов (необязательно)
// 5 - Разделитель учета (необязательно)
// 6 - Фильтр по субконто (если нужно)
// 7 - Обратный порядок (1 - с конца в начало)
Если Опер.ВыбратьОперацииСПроводками(ДатаНачала, ДатаКонца, ТекущийСчет, , , , 1) = 1 Тогда
Пока Опер.ПолучитьПроводку() = 1 Цикл
// Так как установлен обратный порядок, первая же полученная проводка будет самой последней
ПоследнийДокумент = Опер.Документ;
Сообщить("Последнее движение сделал документ: " + ПоследнийДокумент);
// Прерываем цикл, так как цель достигнута
Прервать;
КонецЦикла;
КонецЕсли;
Важно помнить, что этот метод работает достаточно быстро, если задан конкретный счет. Однако фильтрация по сложным наборам субконто здесь может быть менее гибкой, чем в бухгалтерских итогах.
Если ваша конфигурация работает на SQL-версии 1С 7.7 и используются внешние компоненты (например, 1C++), это самый быстрый способ из возможных. Проанализируем структуру базы данных: все проводки хранятся в таблице 1SENTRY. Для поиска последней записи нам критически важна колонка DATE_TIME_DOCID. Это уникальный составной индекс, включающий в себя дату, время и внутренний идентификатор документа, что гарантирует правильную хронологическую последовательность.
Разберем логику построения запроса:
TOP 1).ACID).SC*).DATE_TIME_DOCID.Пример концептуального запроса:
SELECT TOP 1
s.IDDOC as [Документ $Документ]
FROM
1SENTRY as s
WHERE
s.ACID = :СчетИД
AND s.DATE_TIME_DOCID BETWEEN :НачалоПериода AND :КонецПериода
ORDER BY
s.DATE_TIME_DOCID DESC
Этот метод работает мгновенно даже на базах объемом в десятки гигабайт, так как SQL-сервер использует индексы для поиска и сортировки.
Если использование прямых запросов невозможно, а объект Операция по каким-то причинам не подходит, мы можем использовать механизм "черных запросов". Несмотря на то, что сам запрос в 1С 7.7 не умеет выдавать периоды в обратном порядке, мы можем использовать постобработку.
Выполним следующие шаги:
Запрос.Группировка Период; (или Группировка Документ;).ТаблицуЗначений с помощью метода Выгрузить(ТЗ, 0, 0).Сортировать.Посмотрим на пример реализации:
ТекстЗапроса = "
|Период с ДатаНач по ДатаКон;
|ТекСчет = Счет.Основной;
|Док = Операция.Документ;
|Группировка Документ;
|Условие (ТекСчет = ВыбСчет);
|";
Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ);
// Сортируем таблицу по колонке Документ (или Дата) в обратном порядке
// Символ "-" после названия колонки означает убывание
ТЗ.Сортировать("Документ-");
// Первая строка таблицы теперь содержит самый свежий документ
Если ТЗ.КоличествоСтрок() > 0 Тогда
ТЗ.ВыбратьСтроку(1);
ПоследнийДокумент = ТЗ.Документ;
КонецЕсли;
КонецЕсли;
В ситуациях, когда данных крайне много, а мы работаем исключительно через БухгалтерскиеИтоги, можно применить алгоритм уменьшения интервала. Вместо того чтобы рассчитывать итоги за весь период (например, год) и перебирать тысячи проводок, мы можем действовать итеративно.
Рассмотрим этот алгоритм подробнее:
Рассчитать для этого короткого интервала.Выясним причину эффективности этого метода: 1С 7.7 гораздо быстрее обрабатывает итоги за короткий промежуток времени, а вероятность того, что движения были недавно, обычно выше. Это избавляет систему от необходимости строить огромную временную таблицу проводок за весь период.
Для очень больших периодов (например, поиск документа за 10 лет работы предприятия) можно использовать метод дихотомии. Мы делим искомый период пополам и проверяем наличие оборотов во второй половине. Если они есть — продолжаем искать во второй половине (деля её еще раз пополам). Если оборотов нет — переходим к поиску в первой половине. Это позволяет найти день, в котором было совершено последнее движение, за логарифмическое количество шагов (для 10 лет это займет всего около 12-15 итераций расчета итогов).
Важный нюанс: при использовании штатных БухгалтерскихИтогов помните, что метод ВыбратьПериоды(1, 1) позволяет детализировать выборку до конкретных проводок (операций). Если вы уже спозиционировались на нужном счете и субконто, последовательный перебор в цикле Пока ПолучитьПериод() = 1 все равно будет идти от прошлого к будущему. Поэтому, если ваша цель — только один последний документ, всегда ищите способы "развернуть" выборку или ограничить её минимально возможным интервалом дат.