Работа с прямыми запросами в 1С:Предприятии 7.7 (через внешние компоненты, такие как 1C++ или Rainbow) требует от разработчика глубокого понимания структуры таблиц базы данных на уровне SQL-сервера. Одной из самых частых задач является выборка документов с определенным статусом: проведенные, непроведенные или помеченные на удаление. В стандартных объектных запросах 1С мы привыкли к простым флагам, но в SQL-таблице _1SJOURN (системный журнал документов) эти данные хранятся в виде битовых масок и числовых значений.
В данной статье мы разберем, почему возникают ошибки при фильтрации документов, проанализируем структуру системных полей и составим корректные запросы для различных бизнес-задач.
Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики при написании условий в секции WHERE. Представим, что нам нужно выбрать одновременно и проведенные, и непроведенные документы, но исключить помеченные на удаление. Ошибочный код может выглядеть так:
|WHERE
| Журнал.CLOSED & 1 = 1
|AND Журнал.CLOSED & 1 = 0
|AND Журнал.ISMARK = 0
Проанализируем этот фрагмент. Оператор AND требует одновременного выполнения всех условий. Однако документ не может быть одновременно проведенным (CLOSED & 1 = 1) и непроведенным (CLOSED & 1 = 0). Это логическое противоречие, в результате которого SQL-сервер всегда будет возвращать пустой результат. Если нам нужны обе категории документов, использование фильтра по CLOSED в таком виде бессмысленно или требует оператора OR со скобками.
В таблице _1SJOURN поле CLOSED является числовым и содержит в себе сразу несколько признаков состояния документа, упакованных в биты. Для извлечения нужного признака в SQL используется побитовое "И" (оператор &).
CLOSED & 1 равен 1, то документ проведен. Если 0 — не проведен.Выясним, как правильно составить условия:
Журнал.CLOSED & 1 = 1Журнал.CLOSED & 1 = 0CLOSED & 1 можно просто опустить.Поле ISMARK отвечает за пометку документа на удаление. В SQL-версии 1С 7.7 это поле обычно имеет числовой тип (например, tinyint). Распространенная ошибка — попытка сравнить его с пустой строкой ISMARK = ''. Это приводит к некорректной работе запроса или его значительному замедлению, так как SQL-серверу приходится выполнять неявное преобразование типов для каждой строки таблицы.
Правильные значения для ISMARK:
ISMARK = 1 — документ помечен на удаление.ISMARK = 0 — документ не помечен на удаление.Если ваша задача — получить список всех "живых" документов (и тех, что проведены, и тех, что просто записаны), то достаточно одного условия по полю ISMARK. Логика проста: если документ не помечен на удаление, он либо проведен, либо нет. Третьего не дано.
|WHERE
| Журнал.ISMARK = 0
|AND Журнал.iddocdef = $ВидДокумента.Реализация
|AND Журнал.date_time_iddoc BETWEEN :НачДата AND :КонДата~
В данном случае условие (CLOSED & 1 = 1) OR (CLOSED & 1 = 0) является избыточным (тавтологией), так как оно истинно для любой строки. Его исключение повысит производительность запроса.
Иногда требуется найти "забытые" документы — те, которые были созданы, но не проведены и при этом не удалены. В этом случае мы объединяем два условия через оператор AND:
|WHERE
| Журнал.CLOSED & 1 = 0
|AND Журнал.ISMARK = 0
|AND Журнал.iddocdef = $ВидДокумента.Реализация
Здесь мы четко указываем SQL-серверу: "Выбери записи, где первый бит поля CLOSED равен нулю И флаг удаления равен нулю".
Проанализируем, как SQL-сервер обрабатывает такие запросы. Таблица _1SJOURN в 1С 7.7 имеет критически важный индекс по полям IDDOCDEF и DATE_TIME_IDDOC. При написании прямого запроса крайне желательно придерживаться следующей структуры в блоке WHERE:
IDDOCDEF (вид документа), если выборка идет по конкретному типу.DATE_TIME_IDDOC.ISMARK, CLOSED и общим реквизитам.Посмотрим на пример правильного структурированного запроса с использованием метасимволов 1C++:
ТекстЗапроса = "
|SELECT
| Журнал.iddoc as [Док $Документ],
| Журнал.iddocdef as Док_вид,
| $Реализация.Контрагент as [Контрагент $Справочник.Контрагенты]
|FROM
| _1SJOURN as Журнал
|INNER JOIN
| $Документ.Реализация as Реализация ON Журнал.iddoc = Реализация.iddoc
|WHERE
| Журнал.iddocdef = $ВидДокумента.Реализация
| AND Журнал.date_time_iddoc BETWEEN :НачДата AND :КонДата~
| AND Журнал.ISMARK = 0
| AND $Реализация.Контрагент = :ВыбКонтрагент
|";
Рассмотрим специфические моменты, которые помогут избежать ошибок:
1. Формат даты: Поле DATE_TIME_IDDOC в 1С 7.7 — это строка длиной 23 символа. Она содержит дату в формате YYYYMMDD, время и ID документа. Использование конструкции :НачДата и :КонДата~ в 1C++ автоматически преобразует объект даты 1С в нужный строковый формат SQL для правильного сравнения.
2. Скобки: Если вы все же решили использовать сложные условия с OR, всегда заключайте их в скобки. Например:
AND (Журнал.CLOSED & 1 = 0 OR Журнал.ISMARK = 1)
Без скобок приоритет оператора AND может нарушить логику всего запроса, и вы получите непредсказуемый результат.
3. Типы данных: Помните, что $ОбщийРеквизит.Фирма и подобные конструкции разворачиваются в названия полей SQL. Если реквизит имеет тип "Справочник", сравнение должно идти с ID (строкой из 9 символов), а не с наименованием или объектом.
Мы выяснили, что для корректной работы с документами через прямые запросы необходимо:
CLOSED & 1 = 1 для отбора проведенных.ISMARK = 0 для исключения удаленных.A AND NOT A.Соблюдение этих правил обеспечит не только правильность выборки данных, но и высокую скорость работы вашего приложения даже на базах объемом в десятки гигабайт.