При работе с классической связкой 1С 7.7, расширением 1С++ и компонентой 1sqlite разработчики часто используют класс ПоставщикДанных.Справочник (или SQLiteDataProvider) для быстрого вывода больших объемов данных в табличные поля. Это мощный инструмент, позволяющий строить виртуальные списки на основе прямых SQL-запросов к базе данных. Однако существует специфическая проблема, при которой во время поиска по списку (особенно если установлены дополнительные отборы) визуальное отображение элементов ломается: строки начинают дублироваться, на месте найденного элемента отображается последняя запись справочника, а порядок элементов становится хаотичным.
В этой статье мы подробно разберем причины такого поведения, проанализируем механизмы работы индексов SQLite и рассмотрим несколько способов решения проблемы — от корректировки метаданных до манипуляций с версиями программных компонентов.
Рассмотрим ситуацию: на форме справочника размещено табличное поле, работающее через ПоставщикДанных. В коде установлены фильтры по нескольким реквизитам. Пока пользователь просто прокручивает список, всё выглядит нормально. Но как только инициируется поиск (посимвольный по колонке или программное позиционирование на объект), происходит сбой. Если искомый элемент находится за пределами видимости текущего «окна» табличного поля, то при попытке 1С спозиционироваться на него, визуальный список «ломается».
Симптомы сбоя следующие:
Ctrl+PgDn и последующего возврата назад).Проанализируем причины этого явления. Основная проблема заключается в потере детерминированности выборки. Класс ПоставщикДанных запрашивает данные у SQLite порциями (лимитированными выборками). Чтобы табличное поле понимало, какие записи идут до и после текущей, порядок выдачи строк в SQL-запросе должен быть абсолютно предсказуемым и уникальным.
Для корректной работы виртуального списка ТабличноеПоле требует уникальный идентификатор для каждой строки. В классе ПоставщикДанных за это отвечают параметры метода УстановитьТекстЗапроса. Рассмотрим типичный пример вызова этого метода:
ПоставщикДанных.УстановитьТекстЗапроса(ТекстЗапроса, КлючевыеПоля, [ИдПоле]);
Если в качестве КлючевыеПоля указано только индексированное наименование (например, idx_DESCR), а в справочнике есть элементы с одинаковыми названиями, SQLite может возвращать их в разном порядке при выполнении разных подзапросов (для первой страницы, для поиска, для обновления строки). Это приводит к тому, что внутренние индексы поставщика данных «сбиваются».
Решение: Всегда обеспечивайте уникальность ключа. В ключевые поля обязательно нужно добавлять уникальный идентификатор записи (ID).
Выясним причину, связанную с версиями компоненты 1sqlite. Более современные версии (например, сборки от Djelf на базе SQLite 3.36 и выше) обладают агрессивным оптимизатором запросов. Рассмотрим ситуацию, когда у вас в справочнике включен признак «Отбор по реквизиту» для полей, по которым вы фильтруете данные в ПоставщикеДанных.
Когда вы выполняете поиск, SQLite анализирует запрос. Если в условии WHERE указаны поля с отборами, оптимизатор может решить, что использование индекса по этому реквизиту эффективнее, чем использование индекса по наименованию (idx_DESCR), который вы ожидаете для сортировки. В результате выборка данных происходит в порядке, продиктованном индексом отбора, а не индексом сортировки. Как только порядок выдачи строк в SQL перестает совпадать с порядком, который ожидает ТабличноеПоле для визуализации, происходит визуальный «слом» списка.
Проанализируем пример некорректного плана запроса, который может генерировать система:
SELECT ТекущийОбъект.id as [ТекущийЭлемент $Справочник.Контрагенты]
FROM [Справочник.Контрагенты] as ТекущийОбъект
WHERE (ТекущийОбъект.ЗапретОтгрузки = 0 AND ТекущийОбъект.Спящий = 0)
AND ТекущийОбъект.descr >= 'Вайлд'
ORDER BY ТекущийОбъект.idx_DESCR
LIMIT 1
Если для поля ЗапретОтгрузки в конфигураторе 1С включен отбор, SQLite выберет индекс этого поля. Если этот индекс не включает в себя поле наименования, сортировка ORDER BY может быть выполнена некорректно в контексте частичной выборки.
Разберем по шагам, как можно устранить данную ошибку.
Если вам не критично наличие системных индексов 1С для реквизитов, по которым выполняется фильтрация в ПоставщикеДанных, попробуйте отключить признак «Отбор по реквизиту» в Конфигураторе. Это «свяжет руки» оптимизатору SQLite, и он будет вынужден использовать основной индекс (по наименованию или коду), что восстановит стабильность сортировки и поиска. Как показывает практика, именно этот способ часто является самым быстрым решением.
Посмотрим на пример правильной установки текста запроса. Нам нужно гарантировать, что поставщик данных четко идентифицирует каждую строку. Используйте составной ключ в параметре КлючевыеПоля:
// Пример правильной настройки
ТекстЗапроса = "SELECT idx_descr, id [Элемент :Справочник.Контрагенты], descr [Наименование] FROM Справочник_Контрагенты";
КлючевыеПоля = "idx_descr, id"; // Добавляем ID для уникальности
ИдПоле = "Элемент";
ПоставщикДанных.УстановитьТекстЗапроса(ТекстЗапроса, КлючевыеПоля, ИдПоле);
В этом случае, даже если наименования совпадают, связка Наименование + ID всегда будет уникальной, и SQLiteDataProvider сможет однозначно позиционироваться на нужной строке.
Для корректного поиска и сортировки кириллицы крайне важно использовать правильную кодировку (collate). В запросах поиска убедитесь, что вы используете collate _1C. Это гарантирует, что порядок строк в SQLite будет идентичен порядку строк, который ожидает увидеть платформа 1С 7.7.
ТекстЗапросаПоиска = "
|SELECT ТекущийОбъект.id as [ТекущийЭлемент $Справочник.Контрагенты]
|FROM [Справочник.Контрагенты] as ТекущийОбъект
|WHERE ТекущийОбъект.descr >= @ТекстПоиска collate _1C
|ORDER BY ТекущийОбъект.idx_DESCR
|LIMIT 1";
Если вышеуказанные способы не помогают, а вы используете версию 1sqlite 1.0.2.6 или выше, возможно, вы столкнулись с трудноуловимым багом в реализации виртуальных таблиц конкретной сборки. В сообществе разработчиков 1С 7.7 подтверждено, что переход на более старые, «классические» версии компоненты (например, версию от Orefkov) решает проблему без изменения кода. Старые версии имеют менее «умный» оптимизатор, который, тем не менее, работает более предсказуемо в связке с классами 1с++.
Проанализировав ситуацию, мы видим, что проблема «бегающих строк» в 1С 7.7 — это результат нарушения логики между движком базы данных и визуальным компонентом. Для стабильной работы всегда:
ПоставщикеДанных.EXPLAIN QUERY PLAN для отладки).Соблюдение этих правил позволит вам создавать быстрые и надежные интерфейсы на платформе 7.7, которые не уступают по производительности современным версиям 1С.