В процессе разработки и поддержки сложных систем на платформе 1С:Предприятие 8 программисты часто сталкиваются с использованием объекта ХранилищеЗначений. Этот объект позволяет сохранять в базе данных практически любые типы данных: от картинок до сложных структур и табличных документов. Одной из типичных проблем является ситуация, когда данные, извлекаемые из такого хранилища (например, через запрос из регистра сведений), оказываются некорректными или избыточными. Разберем подробно, как локализовать место записи таких данных и устранить причину ошибки.
Рассмотрим конкретный пример: запрос считывает данные из регистра, где в одной из колонок находится ХранилищеЗначений, содержащее ТабличныйДокумент. При получении объекта выясняется, что его свойство ВысотаТаблицы равно 688, хотя ожидается гораздо меньшее количество строк. Это приводит к дублированию записей или выводу пустых областей в интерфейсе.
Выясним возможные причины такого поведения:
Вывести() или Присоединить() может вызываться лишнее количество раз из-за неверных условий в запросе или выборке.ТабличныйДокумент считает своей высотой все строки, к которым было применено хотя бы минимальное оформление (границы, цвет фона, объединение ячеек), даже если в них нет текста.Прежде чем искать код записи, мы должны убедиться, что именно находится внутри объекта. Для этого в режиме отладки или с помощью временного кода проанализируем содержимое. Если мы уже получили объект из хранилища, используем метод Показать().
Рассмотрим пример кода для просмотра данных:
// Предположим, Выборка - это результат запроса к регистру
Если ТипЗнч(Выборка.ТабличныйДокумент) = Тип("ХранилищеЗначения") Тогда
ТабДок = Выборка.ТабличныйДокумент.Получить();
Если ТабДок <> Неопределено Тогда
ТабДок.Показать("Проверка содержимого кэша");
КонецЕсли;
КонецЕсли;
Этот простой шаг позволит визуально определить, где именно начинаются «лишние» 688 строк: это пустые строки с рамками или дублирующиеся данные.
Самая сложная задача — найти, в какой момент и в каком модуле данные упаковываются в ХранилищеЗначений и записываются в регистр. Разберем алгоритм поиска по шагам.
1. Поиск по имени регистра. Проанализируем запрос, который считывает данные. Выясним имя регистра сведений. После этого выполним глобальный поиск (Ctrl+Shift+F) по конфигурации, используя имя этого регистра. Нас интересуют конструкции вида:
РегистрыСведений.ИмяВашегоРегистра.СоздатьМенеджерЗаписи()РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей()ХранилищеЗначения.2. Поиск по ключевым словам механизма. Если мы имеем дело с кэшированием графиков или сетки, используем поиск по строке КэшСетки или ДанныеКэша. Часто за это отвечают специализированные общие модули, такие как УчетРабочегоВремениВнутренний или ГрафикиРаботыКэш.
3. Поиск конструктора хранилища. Данные не могут попасть в базу в «чистом» виде, если колонка имеет тип ХранилищеЗначения. Ищем в коде создание нового объекта:
НовоеХранилище = Новый ХранилищеЗначения(ОбъектДляЗаписи, Новый СжатиеДанных(9));
Установив точку останова на этой строке, мы сможем по стеку вызовов понять, какой бизнес-процесс инициировал обновление данных.
Как только мы нашли процедуру, которая формирует ТабличныйДокумент перед записью, проанализируем её структуру. Обычно это выглядит как цикл по выборке запроса.
Посмотрим на пример типичной ошибки:
// Ошибка может быть здесь
Пока Выборка.Следующий() Цикл
// Если запрос возвращает лишние строки из-за левого соединения,
// высота таблицы будет расти бесконтрольно
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ЗаполнитьЗначенияСвойств(ОбластьСтрока.Параметры, Выборка);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
Проанализируем ситуацию: если высота таблицы зависит от даты (на одну дату правильно, на другую — нет), значит, проблема в данных, которые выбираются запросом для формирования этой таблицы. Проверьте условия ГДЕ в запросе и соединения с таблицами среза последних.
Если мы выяснили, что в регистре уже лежат «битые» данные, их нужно обновить. Рассмотрим два способа:
Способ А: Полная очистка. Можно просто удалить записи из регистра сведений для проблемного периода или объекта. При следующем обращении система (если это предусмотрено логикой конфигурации) увидит, что данных в кэше нет, и сформирует их заново. Если ошибка в коде была исправлена, новые данные будут корректными.
Способ Б: Программное обновление. Напишем небольшую обработку, которая находит нужную запись и перезаписывает её:
МенеджерЗаписи = РегистрыСведений.КэшГрафиковРаботы.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = ПроблемныйОбъект;
МенеджерЗаписи.Дата = ПроблемнаяДата;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
// Вызываем процедуру обновления кэша из общего модуля
ОбновлениеКэшаГлоб.ОбновитьЗаписьВРегистре(МенеджерЗаписи);
КонецЕсли;
Чтобы избежать подобных проблем в будущем, рекомендуем придерживаться следующих правил:
ХранилищеЗначения. Если ТабличныйДокумент.ВысотаТаблицы > 500, это повод для автоматического логирования или генерации предупреждения.Новый СжатиеДанных(9). Это существенно экономит место в базе данных при хранении больших таблиц.Таким образом, решение проблемы всегда сводится к поиску «точки записи» через глобальный поиск по метаданным и последующему анализу запросов, формирующих тело документа. Помните, что ХранилищеЗначений — это лишь контейнер, и искать причину нужно в том коде, который этот контейнер наполняет.