Как в прямом SQL-запросе к базе 1С 7.7 исключить помеченные на удаление или отобрать только проведенные документы?

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

При оптимизации учетных систем на платформе 1С:Предприятие 7.7 разработчики часто прибегают к прямым запросам к базе данных через внешнюю компоненту 1С++ или внешние OLE DB/ODBC провайдеры. Стандартные объектные методы выборки документов работают медленно на больших массивах данных, однако прямой SQL требует четкого понимания структуры системных таблиц, в частности общего журнала документов _1SJOURN.

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

Фильтрация документов, помеченных на удаление

В таблице общего журнала документов за пометку на удаление отвечает поле ISMARK. Проанализируем, как с ним работать в зависимости от используемой СУБД:

  1. Базы данных на MS SQL Server: в таблице _1SJOURN поле ISMARK имеет числовой тип (smallint или tinyint). Значение 0 означает, что документ не помечен на удаление, а значение 1 указывает на наличие пометки на удаление.
  2. Файловые базы данных (DBF): в файле 1SJOURN.DBF поле ISMARK имеет логический тип данных (L). При выполнении прямых запросов через драйверы (например, VFP OLE DB) условие проверяется как ISMARK = .F. или NOT ISMARK.

Посмотрим на базовый пример SQL-запроса для MS SQL, в котором отсекаются все помеченные на удаление документы:


SELECT 
    Журнал.IDDOC AS [Документ $Документ],
    Журнал.DOCNO AS НомерДокумента
FROM 
    _1SJOURN AS Журнал WITH (NOLOCK)
WHERE 
    Журнал.ISMARK = 0

Отбор только проведенных или непроведенных документов

Выясним причину, почему для статуса проведения используется отдельное поле CLOSED и как оно устроено. В платформе 1С 7.7 поле CLOSED представляет собой битовую маску, а не простое числовое значение. Это обусловлено модульной архитектурой платформы, поддерживающей независимую работу компонент «Оперативный учет», «Бухгалтерский учет» и «Расчет».

Младший бит (бит 0 с весом 1) отвечает за общее проведение документа и наличие движений по регистрам. Поэтому для фильтрации проведенных документов применяется операция побитового «И» (&):

  1. Только проведенные документы: условие (Журнал.CLOSED & 1) = 1.
  2. Только непроведенные документы: условие (Журнал.CLOSED & 1) = 0.

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


SELECT 
    Журнал.IDDOC AS [Документ $Документ],
    Журнал.IDDOCDEF AS ВидДокумента,
    Журнал.DOCNO AS НомерДокумента
FROM 
    _1SJOURN AS Журнал WITH (NOLOCK)
WHERE 
    Журнал.ISMARK = 0
    AND (Журнал.CLOSED & 1) = 1

Особый случай: ручные бухгалтерские операции

Проанализируем важную особенность учета: документ конфигурации «Операция» (ручные бухгалтерские проводки) не проводится стандартным механизмом оперативного учета. В таблице _1SJOURN значение поля CLOSED для ручной операции обычно равно 4, то есть первый бит не взведен.

Если в выборку должны входить ручные операции, использование условия (CLOSED & 1) = 1 полностью исключит их из результата. В ситуациях, когда требуется проверять активность проводок бухгалтерской операции, обратимся к таблице проводок _1SENTRY, где признак активности проводки хранится в поле ACTIVE (значение ' ' — один пробел — означает, что проводка активна и включена в баланс).

Оптимизация производительности прямых запросов к журналу

Разберем по шагам правила оптимизации прямых запросов к таблице _1SJOURN, пренебрежение которыми приведет к серьезным просадкам быстродействия сервера:

  1. Использование хинта блокировок: к таблице журнала в запросах на чтение всегда рекомендуется добавлять директиву WITH (NOLOCK), чтобы предотвратить взаимные блокировки с сессиями пользователей, проводящих документы.
  2. Ограничение по индексам: поля ISMARK и CLOSED не являются первыми полями индексов. Кроме того, использование побитовых выражений CLOSED & 1 = 1 делает невозможным использование стандартного B-Tree индекса по данному полю. Запрос без фильтра по дате вызовет полное сканирование таблицы (Table Scan или Index Scan).
  3. Обязательный фильтр по периоду: всегда добавляем ограничение по полю даты и времени позиции документа DATE_TIME_IDDOC, которое входит в основные кластерные индексы: DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~.
  4. Фильтрация по виду документа: при отборе конкретных документов добавляем условие по полю IDDOCDEF. Чтобы не использовать жестко закодированные идентификаторы метаданных, применяем возможности метапарсера 1С++: $ВидДокумента.РасходнаяНакладная.

Посмотрим на итоговый оптимизированный пример прямого запроса для компоненты 1С++:


SELECT 
    CAST(LEFT(Журнал.DATE_TIME_IDDOC, 8) AS DateTime) AS ДатаДокумента,
    Журнал.DOCNO AS НомерДокумента,
    Журнал.IDDOC AS [Док $Документ.Реализация]
FROM 
    _1SJOURN AS Журнал WITH (NOLOCK)
WHERE 
    Журнал.DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~
    AND Журнал.IDDOCDEF = $ВидДокумента.Реализация
    AND Журнал.ISMARK = 0
    AND (Журнал.CLOSED & 1) = 1

Такой подход обеспечивает корректный отбор данных, минимальное потребление ресурсов СУБД и мгновенную обработку выборок любого объема.

← На главную