Как выполнять прямые запросы к DBF-базам в 1С 7.7 с помощью SQLite?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При переносе данных или организации обменов между 1С 7.7 и 1С 8 стандартные механизмы работы с базой данных могут быть недостаточно быстрыми. В базах формата DBF для значительного ускорения выборки данных используется внешняя компонента 1SQLite (библиотека 1sqlite.dll), которая позволяет выполнять прямые SQL-запросы к таблицам информационной базы в памяти.

Разберем основные практические задачи: выборку значений периодических реквизитов, фильтрацию по спискам значений и правильный расчет остатков по регистрам накопления.

1. Получение значений периодических реквизитов на дату

Периодические реквизиты справочников и константы 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.

2. Фильтрация данных по списку значений (Множественный фильтр)

Для передачи условий со списками объектов (например, отобранной номенклатуры) в 1SQLite используется метод УложитьОбъекты(). Он создает во временной таблице SQLite колонку val с внутренними идентификаторами объектов.

Разберем по шагам, как выгрузить СписокЗначений во временную таблицу и применить фильтр в запросе:

  1. Создаем и заполняем СписокЗначений нужными элементами.
  2. Вызываем метод База.УложитьОбъекты(Список, "ВыбНоменклатура", 0, "Номенклатура").
  3. В тексте SQL-запроса связываем данные через оператор IN (SELECT val FROM ВыбНоменклатура).

Посмотрим на пример кода:


Список = СоздатьОбъект("СписокЗначений");
Список.Добавить(ВыбТовар1);
Список.Добавить(ВыбТовар2);

// Помещаем список во временную таблицу базы SQLite
База.УложитьОбъекты(Список, "ВыбНоменклатура", 0, "Номенклатура");

ТекстЗапроса = "
|SELECT 
|    Остатки.Товар [Товар :Справочник.Номенклатура],
|    Остатки.ОстатокТовараОстаток AS Остаток
|FROM [РегистрИтоги.ОстаткиТоваров] AS Остатки
|WHERE Остатки.Товар IN (SELECT val FROM ВыбНоменклатура)
|";

Компонента также позволяет выгружать во временные таблицы не только списки, но и структуры типа ТаблицаЗначений, что удобно для многоколоночных условий.

3. Получение остатков регистров (Архитектура таблиц RG и RA)

Выясним причину, почему остатки в 1С 7.7 нельзя просто брать из одной таблицы на любую произвольную дату. Платформа 1С 7.7 использует раздельное хранение:

  1. Таблица итогов (RG...): хранит рассчитанные остатки на начало каждого расчетного периода (обычно 1-е число месяца), а также текущие итоги на Точку Актуальности (ТА).
  2. Таблица движений (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.

← На главную