В практике разработки на платформе 1С:Предприятие 8 часто встречается ситуация, когда в рамках одного цикла нам необходимо записывать данные в регистр сведений и тут же проверять их наличие с помощью запроса. Казалось бы, логика проста: записали — проверили — увидели. Однако на деле программисты сталкиваются с тем, что запрос упорно возвращает пустой результат, хотя отладка подтверждает успешное выполнение метода Записать(). Разберем подробно, почему это происходит и как правильно организовать работу с данными в подобных сценариях.
Рассмотрим ситуацию, с которой столкнулся автор темы. В цикле происходит обработка файлов. Перед записью информации о файле в регистр АрхивTS выполняется запрос, чтобы убедиться, что такого файла еще нет в базе. Если запрос ничего не находит, создается менеджер записи, заполняются ресурсы и вызывается метод Записать(). На следующей итерации цикла, даже если файл с таким же именем снова попадает в обработку, запрос его «не видит».
Давайте проанализируем основные причины такого поведения системы:
НачатьТранзакцию(), любая операция записи порождает неявную транзакцию. Если база работает в режиме Snapshot Isolation (версионное хранилище), текущий сеанс может видеть «срез» данных, актуальный на момент начала какой-либо внутренней операции, и не замечать изменений до завершения определенного процесса.РегистрСведенийМенеджерЗаписи является довольно «тяжелым» и работает медленнее, чем наборы записей, так как выполняет целый ряд проверок и блокировок при каждой итерации.Это самый эффективный и производительный способ, к которому в итоге пришел автор вопроса. Вместо того чтобы на каждой итерации «мучить» базу данных и СУБД запросами, мы создаем структуру данных в оперативной памяти (коллекцию), которая будет гарантированно содержать все новые записи.
Рассмотрим алгоритм реализации:
ТаблицаЗначений.Найти().Посмотрим на пример кода:
// 1. Подготовка данных перед циклом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АрхивTS.ИмяTSФайлаСРасширением КАК ИмяФайла
|ИЗ
| РегистрСведений.АрхивTS КАК АрхивTS";
ТаблицаКэш = Запрос.Выполнить().Выгрузить();
ТаблицаКэш.Индексы.Добавить("ИмяФайла");
// 2. Основной цикл обработки
Для Каждого ТекущийФайл Из СписокФайлов Цикл
// Ищем в локальном кэше (это работает мгновенно и всегда видит новые данные)
ПараметрыПоиска = Новый Структура("ИмяФайла", ТекущийФайл.Имя);
НайденныеСтроки = ТаблицаКэш.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
Продолжить; // Файл уже обрабатывался
КонецЕсли;
// 3. Запись в регистр
МенеджерЗаписи = РегистрыСведений.АрхивTS.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИмяTSФайлаСРасширением = ТекущийФайл.Имя;
МенеджерЗаписи.Период = ТекущаяДата();
МенеджерЗаписи.Записать();
// 4. Обновление локального кэша
НоваяСтрока = ТаблицаКэш.Добавить();
НоваяСтрока.ИмяФайла = ТекущийФайл.Имя;
КонецЦикла;
Если по каким-то причинам использование таблицы значений невозможно (например, объем данных слишком велик для оперативной памяти), мы можем заставить платформу 1С и СУБД игнорировать кэш и читать самые актуальные данные прямо из таблиц базы данных. Для этого в тексте запроса используется конструкция ДЛЯ ИЗМЕНЕНИЯ.
Проанализируем ситуацию: когда мы добавляем это ключевое слово, система устанавливает исключительную блокировку на читаемые записи (или диапазон). Это гарантирует, что запрос обратится к реальным данным СУБД, минуя любые оптимизационные прослойки кэша сеанса.
Пример изменения текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АрхивTS.ИмяTSФайлаСРасширением КАК ИмяTSФайлаСРасширением
|ИЗ
| РегистрСведений.АрхивTS КАК АрхивTS
|ГДЕ
| АрхивTS.ИмяTSФайлаСРасширением = &ИмяTSФайлаСРасширением
|ДЛЯ ИЗМЕНЕНИЯ"; // Ключевое слово для актуализации данных
Запрос.УстановитьПараметр("ИмяTSФайлаСРасширением", ИмяТекущегоФайла);
Результат = Запрос.Выполнить();
Важно: Использование ДЛЯ ИЗМЕНЕНИЯ значительно повышает нагрузку на СУБД и может приводить к блокировкам, если несколько пользователей одновременно выполняют подобный код. Используйте этот метод с осторожностью.
Объект РегистрыСведенийНаборЗаписей работает более прямолинейно по сравнению с МенеджерЗаписи. При записи набора платформа выполняет более «жесткую» фиксацию изменений. Если ваша задача позволяет, попробуйте заменить создание менеджера записи на работу с набором из одной записи.
Рассмотрим пример:
Набор = РегистрыСведений.АрхивTS.СоздатьНаборЗаписей();
Набор.Отбор.Период.Установить(ТекущийПериод);
Набор.Отбор.ИмяTSФайлаСРасширением.Установить(ИмяTS_ФайлаУНас);
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Период = ТекущийПериод;
НоваяЗапись.ИмяTSФайлаСРасширением = ИмяTS_ФайлаУНас;
// Запись набора часто форсирует обновление состояния данных для текущего контекста
Набор.Записать();
Если проблема сохраняется, выполним проверку следующих моментов:
Строка(50), а вы передаете параметр длиной 100 символов, СУБД может обрезать пробелы или проводить сравнение некорректно. Используйте функцию СокрЛП() перед передачей параметра в запрос.ПриЗаписи), данные не будут физически зафиксированы в базе до самого конца транзакции. В этом случае другие сеансы их не увидят, а текущий сеанс может вести себя непредсказуемо в зависимости от настроек СУБД.Подводя итог, можно сказать, что лучшей практикой является вынос запроса за пределы цикла и использование ТаблицыЗначений для накопления результатов в памяти. Это не только решает проблему «невидимости» данных, но и ускоряет работу программы в десятки раз, минимизируя количество обращений к серверу базы данных.