Как написать прямой SQL-запрос вместо Бухгалтерских Итогов в 1С 7.7 для получения ОС и МОЛ?

Программист 1С v7.7 Бухгалтерский учет Промышленность, строительство и АПК
← На главную

В процессе оптимизации и ускорения отчетов в системе 1С:Предприятие 7.7 разработчики часто сталкиваются с необходимостью замены стандартного механизма БухгалтерскиеИтоги на прямые SQL-запросы к базе данных. Особенно остро эта проблема встает в базах данных крупного размера, где расчет остатков по субконто «Основные средства» и «Материально-ответственные лица (МОЛ)» штатными средствами может занимать минуты.

В этой статье мы подробно разберем, как устроена структура хранения бухгалтерских итогов в SQL-базе 1С 7.7, проанализируем типичные ошибки при написании таких запросов и рассмотрим два правильных способа решения задачи: «чистый» SQL-запрос и профессиональное решение с использованием внешней компоненты 1С++.

---

Анализ структуры таблиц бухгалтерского учета в 1С 7.7

Для того чтобы написать эффективный и быстрый SQL-запрос, нам необходимо отказаться от привычных объектов метаданных 1С и понять, как данные хранятся на уровне СУБД. Бухгалтерский учет в версии 7.7 опирается на три ключевые таблицы:

  1. _1SACCS (План счетов): Содержит информацию о счетах, их кодах (SCHKOD), признаках активности (ACTIVE) и идентификаторах (ID).
  2. _1SBKTTL (Таблица итогов): Хранит рассчитанные остатки и обороты в разрезе периодов (кварталов). Именно сюда 1С записывает свернутые данные для ускорения работы. Здесь находятся поля субконто SC0, SC1, SC2 и соответствующие им виды субконто VSC0, VSC1, VSC2.
  3. _1SENTRY (Проводки / Журнал операций): Содержит детальные записи о каждой проводке. Поля ACCDTID (счет дебета), ACCKTID (счет кредита), DTSC0–DTSC2 (субконто дебета), KTSC0–KTSC2 (субконто кредита) и сумма SUM_ являются основными для получения оборотов за произвольный период.

Важно помнить, что физическое расположение конкретного субконто в полях SC0, SC1 или SC2 таблицы итогов жестко привязано к порядку субконто на счете в конфигураторе. Если для счета 01 «Основные средства» первым субконто задан справочник «Основные средства», а вторым — «Места хранения» (МОЛ), то идентификатор ОС всегда будет записываться в поле SC0, а идентификатор МОЛ — в поле SC1.

---

Разбор критических ошибок в подходе автора темы

Проанализируем фрагмент кода, представленный в Сообщении 15 темы форума. В нем автор совершает несколько грубых архитектурных ошибок:

  1. Использование таблиц документов (DH*): Автор пытается связать проводки с таблицами конкретных документов перемещения и ввода в эксплуатацию (через UNION ALL). Это в корне неверно. Все проводки по счетам учета ОС уже автономно содержат ссылки на объекты субконто непосредственно в полях таблицы _1SENTRY. Обращение к таблицам документов приводит к колоссальному падению производительности и потере универсальности (запрос не увидит проводки ручных операций или новых видов документов).
  2. Соединение таблиц по текстовому наименованию (DESCR): Конструкции вида ON (СпрОС.DESCR = БИ.НаименованиеСубконто1) недопустимы в SQL. Поиск и слияние строк по текстовым полям происходят крайне медленно, а при наличии дублей в наименованиях ОС вы получите некорректно удвоенные или утроенные суммы. Соединение необходимо выполнять строго по уникальным буквенно-цифровым идентификаторам 1С типа char(9) (поля ID).

---

Как перехватить «сырой» SQL-запрос от платформы 1С

Для того чтобы посмотреть, какой именно SQL-запрос генерирует сама платформа при вызове методов БухгалтерскиеИтоги, выполните следующие действия:

  1. Запустите утилиту Microsoft SQL Server Profiler на сервере СУБД.
  2. Создайте новое подключение к вашему серверу (New Trace) и выберите базу данных 1С в фильтрах (параметр DatabaseName).
  3. Включите отслеживание событий SQL:BatchCompleted и RPC:Completed.
  4. Запустите формирование штатного отчета в режиме предприятия 1С 7.7.
  5. В окне профайлера отобразится точный текст T-SQL запроса со всеми временными таблицами, который платформа отправляет серверу при выполнении метода ВыполнитьЗапрос(). Это даст вам готовую пищу для анализа структуры запроса под вашу конфигурацию.

---

Решение 1. «Чистый» SQL-запрос для получения остатков ОС по МОЛ

Рассмотрим правильный алгоритм получения остатков ОС в разрезе МОЛ без использования сторонних компонент. Нам потребуется определить остатки на начало ближайшего квартала из таблицы _1SBKTTL и скорректировать их оборотами из таблицы _1SENTRY за период от начала квартала до требуемой даты.

Проанализируем пример такого SQL-запроса:


DECLARE @DateTarget datetime;
DECLARE @DateQuarterBegin datetime;
DECLARE @Acc01 ID char(9);
DECLARE @MolID char(9);

-- Задаем параметры (идентификаторы счетов и субконто предварительно получаем из метаданных)
SET @DateTarget = '20231015'; -- Дата, на которую нужны остатки
SET @DateQuarterBegin = '20231001'; -- Начало квартала, в который попадает дата
SET @Acc01 = '   1R   '; -- Внутренний ID счета 01.1 в таблице _1SACCS
SET @MolID = '   2W0   '; -- Идентификатор искомого МОЛ в справочнике SC208

-- Выбираем остатки ОС по конкретному МОЛ
SELECT 
    ResultTable.OsId AS [Идентификатор_ОС],
    SprOS.DESCR AS [Наименование_ОС],
    SUM(ResultTable.BalanceDebet) AS [Остаток_Дт]
FROM (
    -- Шаг 1: Получаем остатки на начало квартала из таблицы итогов
    SELECT 
        SC0 AS OsId,
        SD AS BalanceDebet
    FROM _1SBKTTL (NOLOCK)
    WHERE ACCID = @Acc01 
      AND DATE = @DateQuarterBegin 
      AND KIND = 1 -- признак аналитических итогов
      AND SC1 = @MolID

    UNION ALL

    -- Шаг 2: Прибавляем дебетовые обороты с начала квартала по целевую дату
    SELECT 
        DTSC0 AS OsId,
        SUM_ AS BalanceDebet
    FROM _1SENTRY (NOLOCK)
    WHERE ACCDTID = @Acc01 
      AND DATE_TIME_DOCID >= @DateQuarterBegin 
      AND DATE_TIME_DOCID < DATEADD(day, 1, @DateTarget)
      AND DTSC1 = @MolID

    UNION ALL

    -- Шаг 3: Вычитаем кредитовые обороты с начала квартала по целевую дату
    SELECT 
        KTSC0 AS OsId,
        -SUM_ AS BalanceDebet
    FROM _1SENTRY (NOLOCK)
    WHERE ACCKTID = @Acc01 
      AND DATE_TIME_DOCID >= @DateQuarterBegin 
      AND DATE_TIME_DOCID < DATEADD(day, 1, @DateTarget)
      AND KTSC1 = @MolID
) AS ResultTable
INNER JOIN SC11450 AS SprOS (NOLOCK) ON ResultTable.OsId = SprOS.ID
GROUP BY ResultTable.OsId, SprOS.DESCR
HAVING SUM(ResultTable.BalanceDebet) > 0;

Этот запрос отрабатывает мгновенно благодаря использованию кластеризованных индексов по полям дат и счетов. Мы полностью исключили тяжелое чтение документов и непроизводительное текстовое сравнение.

---

Решение 2. Использование класса AccountsRecordset из 1С++

Если в вашей конфигурации зарегистрирована и используется внешняя компонента 1С++, решение задачи упрощается в разы. Нам больше не нужно вручную прописывать сложную логику вычисления квартальных срезов и соединений таблиц остатков и оборотов. Класс AccountsRecordset предоставляет мощный механизм «виртуальных таблиц» (аналог встроенного языка запросов 1С 8.х).

Разберем по шагам пример кода на встроенном языке 1С 7.7 с использованием метазапроса 1С++:


Процедура СформироватьОстаткиОС()
    ДП = СоздатьОбъект("ДанныеРаботы"); // Создаем контекст 1С++
    
    // Инициализируем Прямой Запрос через класс AccountsRecordset
    Запрос = СоздатьОбъект("ODBCRecordset");
    
    ТекстЗапроса = "
    |SELECT 
    |    Рег.Субконто1 AS [ОС $Справочник.ОсновныеСредства],
    |    Рег.Субконто2 AS [МОЛ $Справочник.ФизическиеЛица],
    |    Рег.СуммаОстатокДт AS СуммаОстаток
    |FROM 
    |    $БИ.Остатки.Основной(
    |        :ВыбДата, 
    |        , 
    |        Счет = :ВыбСчет, 
    |        (Субконто2 = :ВыбМОЛ)
    |    ) AS Рег
    |";
    
    Запрос.УстановитьТекстовыйПараметр("ВыбДата", КонДата);
    Запрос.УстановитьТекстовыйПараметр("ВыбСчет", глНайтиСчет("01.1"));
    Запрос.УстановитьТекстовыйПараметр("ВыбМОЛ", ВыбранныйМОЛ);
    
    // Выполняем запрос и выгружаем результат в Таблицу Значений
    ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
    
    // Выводим результат в консоль или таблицу
    ТЗ.ВыбратьСтроки();
    Пока ТЗ.ПолучитьСтроку() = 1 Цикл
        Сообщить("ОС: " + ТЗ.ОС + " | МОЛ: " + ТЗ.МОЛ + " | Остаток: " + ТЗ.СуммаОстаток);
    КонецЦикла;
КонецПроцедуры

В данном примере конструкция $БИ.Остатки.Основной — это мощный виртуальный генератор запросов. Внутри себя компонента 1С++ сама определяет, из каких физических таблиц СУБД (будь то _1SBKTTL или _1SENTRY) нужно собрать данные на указанную дату :ВыбДата, автоматически применяет нужные фильтры по счету и субконто и отдает программисту готовую, типизированную таблицу результатов.

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

← На главную