При оптимизации учетных систем на платформе 1С:Предприятие 7.7 разработчики часто прибегают к прямым запросам к базе данных через внешнюю компоненту 1С++ или внешние OLE DB/ODBC провайдеры. Стандартные объектные методы выборки документов работают медленно на больших массивах данных, однако прямой SQL требует четкого понимания структуры системных таблиц, в частности общего журнала документов _1SJOURN.
Разберем подробно, в каких полях таблицы журнала хранятся признаки пометки на удаление и проведения документов, как правильно составить секцию WHERE и какие нюансы производительности необходимо учитывать при построении прямых запросов.
В таблице общего журнала документов за пометку на удаление отвечает поле ISMARK. Проанализируем, как с ним работать в зависимости от используемой СУБД:
_1SJOURN поле ISMARK имеет числовой тип (smallint или tinyint). Значение 0 означает, что документ не помечен на удаление, а значение 1 указывает на наличие пометки на удаление.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) отвечает за общее проведение документа и наличие движений по регистрам. Поэтому для фильтрации проведенных документов применяется операция побитового «И» (&):
(Журнал.CLOSED & 1) = 1.(Журнал.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, пренебрежение которыми приведет к серьезным просадкам быстродействия сервера:
WITH (NOLOCK), чтобы предотвратить взаимные блокировки с сессиями пользователей, проводящих документы.ISMARK и CLOSED не являются первыми полями индексов. Кроме того, использование побитовых выражений CLOSED & 1 = 1 делает невозможным использование стандартного B-Tree индекса по данному полю. Запрос без фильтра по дате вызовет полное сканирование таблицы (Table Scan или Index Scan).DATE_TIME_IDDOC, которое входит в основные кластерные индексы: DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~.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
Такой подход обеспечивает корректный отбор данных, минимальное потребление ресурсов СУБД и мгновенную обработку выборок любого объема.