При переносе данных или организации обменов между 1С 7.7 и 1С 8 стандартные механизмы работы с базой данных могут быть недостаточно быстрыми. В базах формата DBF для значительного ускорения выборки данных используется внешняя компонента 1SQLite (библиотека 1sqlite.dll), которая позволяет выполнять прямые SQL-запросы к таблицам информационной базы в памяти.
Разберем основные практические задачи: выборку значений периодических реквизитов, фильтрацию по спискам значений и правильный расчет остатков по регистрам накопления.
Периодические реквизиты справочников и константы 1С 7.7 хранятся в таблице 1SCONST (__1S_Const). Для получения среза последних значений на дату документа или произвольную дату сформируем подзапрос, выбирающий запись с максимальной датой, меньшей либо равной целевой. Для ограничения выборки одной строкой в SQLite используется оператор LIMIT 1 (вместо привычного для T-SQL TOP 1).
Посмотрим на пример выборки номенклатуры и цены из периодического реквизита:
Процедура Сформировать()
ТекстЗапроса = "
|SELECT
| Док.Номенклатура [Номенклатура :Справочник.Номенклатура],
| Док.Количество Количество,
| (
| SELECT
| Периодика.value
| FROM __1S_Const Периодика
| INNER JOIN [Справочник.Цены] AS Цены
| ON Цены.parentext = Док.Номенклатура
| AND $Цены.ТипЦен = :ТипЦен
| WHERE Периодика.objid = Цены.id
| AND Периодика.id = :ИсторияРеквизита.Цены.Цена
| AND Периодика.date <= Жур.date
| ORDER BY Периодика.date DESC
| LIMIT 1
| ) AS Цена
|FROM [ДокументСтроки.ПоступлениеТМЦ] Док
|INNER JOIN [Журнал] Жур
| ON Док.iddoc = Жур.iddoc
| AND Жур.date BETWEEN :НачДата AND :КонДата
|";
Попытка
База = СоздатьОбъект("SQLiteBase");
Исключение
ЗагрузитьВнешнююКомпоненту("1sqlite.dll");
База = СоздатьОбъект("SQLiteBase");
КонецПопытки;
// Открываем базу данных в памяти
База.Открыть(":memory:");
Запрос = База.НовыйЗапрос();
Запрос.Подставлять("ТипЦен", ТипЦен);
Запрос.Подставлять("НачДата", НачДата);
Запрос.Подставлять("КонДата", КонДата);
ТЗ = Запрос.ВыполнитьЗапрос(ТекстЗапроса);
ТЗ.ВыбратьСтроку();
КонецПроцедуры
Важный нюанс: если периодический реквизит изменяется не только вручную на дату, но и документами в течение дня, в таблице 1SCONST заполняется поле документа-регистратора DOCID. В таких случаях для точного среза необходимо дополнительно учитывать позицию документа через журнал 1SJOURN.
Для передачи условий со списками объектов (например, отобранной номенклатуры) в 1SQLite используется метод УложитьОбъекты(). Он создает во временной таблице SQLite колонку val с внутренними идентификаторами объектов.
Разберем по шагам, как выгрузить СписокЗначений во временную таблицу и применить фильтр в запросе:
СписокЗначений нужными элементами.База.УложитьОбъекты(Список, "ВыбНоменклатура", 0, "Номенклатура").IN (SELECT val FROM ВыбНоменклатура).Посмотрим на пример кода:
Список = СоздатьОбъект("СписокЗначений");
Список.Добавить(ВыбТовар1);
Список.Добавить(ВыбТовар2);
// Помещаем список во временную таблицу базы SQLite
База.УложитьОбъекты(Список, "ВыбНоменклатура", 0, "Номенклатура");
ТекстЗапроса = "
|SELECT
| Остатки.Товар [Товар :Справочник.Номенклатура],
| Остатки.ОстатокТовараОстаток AS Остаток
|FROM [РегистрИтоги.ОстаткиТоваров] AS Остатки
|WHERE Остатки.Товар IN (SELECT val FROM ВыбНоменклатура)
|";
Компонента также позволяет выгружать во временные таблицы не только списки, но и структуры типа ТаблицаЗначений, что удобно для многоколоночных условий.
Выясним причину, почему остатки в 1С 7.7 нельзя просто брать из одной таблицы на любую произвольную дату. Платформа 1С 7.7 использует раздельное хранение:
RG...): хранит рассчитанные остатки на начало каждого расчетного периода (обычно 1-е число месяца), а также текущие итоги на Точку Актуальности (ТА).RA...): содержит детальные записи прихода и расхода, привязанные к документам.Проанализируем две ситуации расчета остатков:
Вариант А: Получение актуального остатка на Точку Актуальности (ТА)
Если требуется получить остаток на ТА, достаточно обратиться только к таблице итогов с отбором по дате последнего рассчитанного периода итогов:
МД = СоздатьОбъект("MetaDataWork");
НачПериод = МД.ПолучитьНачПериода(ТекущаяДата()); // Начало расчетного периода
Запрос = База.НовыйЗапрос();
Запрос.Подставлять("ПредПериод", НачПериод);
ТекстЗапроса = "
|SELECT
| Итоги.Товар [Товар :Справочник.Номенклатура],
| SUM(Итоги.ОстатокТовара) AS Остаток
|FROM [РегистрИтоги.ОстаткиТоваров] AS Итоги
|WHERE Итоги.period = :ПредПериод
| AND Итоги.Товар IN (SELECT val FROM НомТемп)
|GROUP BY Итоги.Товар
|";
Вариант Б: Получение остатка на произвольную дату в прошлом
Если дата запроса не совпадает с ТА и находится внутри периода, платформа требует сложить остаток на начало месяца из таблицы итогов (RG) и движения из таблицы (RA) с начала месяца по целевую дату (Приход со знаком плюс, Расход со знаком минус).
Чтобы не писать сложные конструкции объединения таблиц вручную, в библиотеке 1С++ рекомендуется использовать класс ПрямойЗапрос (DirectQuery). Он поддерживает виртуальные таблицы остатков (например, $РегистрОстатки...), которые автоматически транслируются в оптимальный SQL-код как для DBF (через 1SQLite), так и для MS SQL Server.