Работа с большими объемами данных в устаревшей платформе 1С 7.7 часто становится «бутылочным горлышком» для производительности системы. Стандартные списки справочников и журналов документов при росте базы начинают работать медленно, особенно если требуется динамический вывод остатков или цен. Одним из наиболее мощных инструментов решения этой проблемы является использование компоненты 1С++ и класса Поставщик данных (Data Provider). В этой статье мы разберем, как максимально эффективно внедрить этот инструмент, избежать типовых ошибок и оптимизировать SQL-запросы.
Первый вопрос, который возникает у разработчика: можно ли полностью заменить стандартную табличную часть формы списка на ТабличноеПоле, работающее через ПоставщикДанных? Рассмотрим эту ситуацию подробнее. Платформа 1С 7.7 жестко привязана к своим внутренним механизмам отображения данных, если форма объявлена как «ФормаСписка». Однако, использование ПоставщикаДанных позволяет полностью перехватить логику отображения.
Рекомендация: Да, удаление стандартной табличной части и установка объекта ТабличноеПоле (ActiveX от 1С++) является правильным решением для высоконагруженных систем. Платформа не будет «дергать» лишние данные из справочника, так как управление отрисовкой и выборкой полностью переходит к классу ПоставщикДанных. Это позволяет избежать избыточного чтения данных через штатный движок DBF или SQL-запросы самой платформы.
Одной из самых ресурсоемких задач является вывод актуальных остатков или цен в форме подбора или списка номенклатуры. Рассмотрим два подхода к решению этой задачи и проанализируем их эффективность.
Подход 1: Вычисление при выводе строки. Многие привыкли использовать текстовые колонки с формулами, которые вызывают функцию ПолучитьОстатки(). В контексте ПоставщикаДанных это событие ПриВыводеСтроки. Однако, выполнение параметризованного запроса в цикле (для каждой видимой строки) — это худшее, что можно придумать для производительности сервера. Даже если записей на экране всего 20, постоянные мелкие запросы создают избыточную нагрузку на сеть и SQL-сервер.
Подход 2: Соединение данных (Join) в основном запросе. Это наиболее правильный путь. Мы сразу присоединяем к основному запросу справочника необходимые данные из регистров или таблиц цен. Посмотрим на пример оптимизированного SQL-запроса, который использует OUTER APPLY для получения последней цены номенклатуры (актуально для MS SQL 2005 и выше):
select
товары.id as [Товар $Справочник.Номенклатура],
coalesce(cast(цены.Цена as numeric(10,2)), 0) as [Цена $Число]
from
$Справочник.Номенклатура as товары with (nolock)
outer apply (
select top 1
спрЦены.Цена as Цена
from
$Справочник.Цены as спрЦены with (nolock)
where
спрЦены.parentext = товары.id
and $спрЦены.ТипЦены = :НужныйТипЦен
order by
спрЦены.id desc -- или по дате/времени, если есть периодичность
) as цены
Преимущество этого метода в том, что ПоставщикДанных сначала ограничивает выборку (например, только те 30 строк, которые видны пользователю), и только для этих строк SQL-сервер выполняет вложенный расчет. Это на порядки быстрее, чем вызывать функции 1С для каждой ячейки.
Иногда данные для вывода настолько сложны, что их трудно объединить в один SQL-запрос. В этом случае проанализируем вариант с использованием Индексированной Таблицы Значений (ИТЗ). Мы можем один раз выгрузить все нужные остатки в ИТЗ с индексом по объекту (номенклатуре) и при выводе строки просто подтягивать значение из памяти.
Чтобы данные оставались актуальными, выясним, как правильно организовать обновление:
ПерерисоватьСтроки у табличного поля. Это заставит форму обновиться, не сбрасывая текущую позицию курсора и не перечитывая основной запрос поставщика данных.Многие разработчики сталкиваются с тем, что в стандартном классе ПоставщикДанных.Справочник не работает быстрый поиск по коду, если код текстовый. Проанализируем причину: класс может неправильно форматировать строку поиска, добавляя лишние пробелы или не учитывая тип кода в метаданных.
Разберем решение этой проблемы. Необходимо внести изменения в функцию БыстрыйПоискПоПодстроке() внутри класса. Рассмотрим исправленный фрагмент кода:
Если МетаСправочник.ТипКода = "Числовой" Тогда
ТекстЗапросаПоиска = ТекстЗапросаПоиска + "
|" + КлючевоеСлово + " ТекущийОбъект.code = '" + Формат(Значение, "Ч" + МетаСправочник.ДлинаКода + " ") + "'
|";
Иначе
-- Для текстового кода используем обрезку пробелов и поиск по началу строки
ТекстЗапросаПоиска = ТекстЗапросаПоиска + "
|" + КлючевоеСлово + " substring(ТекущийОбъект.code, 1, " + СтрДлина(СокрЛП(Значение)) + ") = '" + СокрЛП(Значение) + "'
|";
КонецЕсли;
Важно помнить, что функция substring в SQL (или substr в SQLite) позволяет эффективно искать по первым символам кода, что эмулирует стандартное поведение 1С, но на гораздо более высокой скорости.
Если вам требуется реализовать дерево групп (как в типовых формах списка 7.7), рассмотрим механизм синхронизации двух объектов ТабличноеПоле. Одно поле настраивается на отображение только групп (фильтр по полю IsFolder = 1), а второе — на отображение элементов выбранной группы. При активации строки в дереве групп мы просто меняем параметр ParentID в SQL-запросе основного списка и вызываем Обновить().
Что касается вычисляемых колонок, которые не привязаны к данным SQL напрямую (например, статус, зависящий от сложной логики в коде 1С), добавим их программно:
Колонки = ПоставщикДанных.ТабличноеПоле.Колонки;
НоваяКол = Колонки.Добавить("Статус");
-- В событии ПриВыводеСтроки заполняем ячейку
ОформлениеСтроки.Ячейки.Статус.УстановитьТекст(ВычислитьСтатус(ДанныеСтроки.ID));
Важный момент: Если колонка невидима в данный момент, ПоставщикДанных не будет вызывать событие ПриВыводеСтроки для этой ячейки, что экономит ресурсы. Как только пользователь включит видимость колонки, табличное поле само инициирует перерисовку.
Используя эти методы, вы сможете превратить неповоротливые списки 1С 7.7 в современные, мгновенно работающие интерфейсы, способные обрабатывать миллионы записей без задержек.