Как выбрать документы по дате в прямом запросе 1С++ (1С 7.7)?

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

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

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

Архитектурные особенности хранения документов в 1С 7.7

Для начала проанализируем, как платформа 1С 7.7 хранит информацию о документах на уровне СУБД (MS SQL Server или DBF-файлов). Информация о каждом документе разделена на две части:

  1. Общие реквизиты и метаданные заголовка: дата, время, номер, пометка удаления, признак проведения и уникальный идентификатор вида документа. Все эти данные хранятся в одной общей системной таблице — _1SJOURN (Общий журнал документов).
  2. Специфические реквизиты документа: реквизиты шапки и табличной части конкретного вида документа (например, ЛицевойСчет в документе Перерасчет) хранятся в обособленной таблице вида $Документ.Перерасчет (на уровне SQL это таблица с именем вида _1S[Идентификатор]).

Таким образом, чтобы получить документ с его специфическими реквизитами и одновременно отфильтровать его по дате создания, нам необходимо выполнить операцию соединения (INNER JOIN) таблицы конкретного документа с таблицей общего журнала _1SJOURN по уникальному 9-символьному идентификатору документа IDDOC.

Структура поля DATE_TIME_IDDOC

В таблице общего журнала документов _1SJOURN нет привычного поля типа DateTime для хранения даты документа. Вместо этого используется составное текстовое поле DATE_TIME_IDDOC длиной 23 символа. Понимание его структуры критически важно для написания корректных запросов:

Именно из-за текстового формата поля DATE_TIME_IDDOC простая передача стандартного параметра даты СУБД приведет к ошибке несовместимости типов или к неверным результатам выборки.

Шаг 1: Базовое соединение таблиц

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


SELECT 
    Журнал.IDDOC [Документ $Документ.Перерасчет],
    $Перерасчет.ЛицевойСчет [ЛицевойСчет $Справочник.ЛицевыеСчета]
FROM 
    $Документ.Перерасчет AS Перерасчет
INNER JOIN 
    _1SJOURN AS Журнал ON Перерасчет.IDDOC = Журнал.IDDOC

В данном примере мы связали таблицы по полю IDDOC. Обратите внимание на типизацию в квадратных скобках: конструкция [Документ $Документ.Перерасчет] указывает метапарсеру 1С++, что поле необходимо преобразовать в соответствующий объект ссылки 1С.

Шаг 2: Фильтрация по дате с использованием модификаторов 1С++

Для отбора документов по периоду нам необходимо наложить условие BETWEEN на строковое поле DATE_TIME_IDDOC. Передавать параметры напрямую в виде строк крайне неудобно и небезопасно с точки зрения SQL-инъекций. 1С++ предлагает элегантное решение — использование модификаторов параметров (тильды).

Разберем разницу между модификаторами дат в 1С++:

Посмотрим на пример условия фильтрации:

WHERE Журнал.DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~

Шаг 3: Оптимизация производительности запроса через IDDOCDEF

При работе с большими базами данных скорость выполнения прямого запроса имеет решающее значение. В таблице _1SJOURN СУБД MS SQL создает кластерный индекс, построенный по полям DATE_TIME_IDDOC и IDDOCDEF (идентификатор вида документа).

Если в запросе использовать только соединение по IDDOC и фильтрацию по дате, планировщик запросов SQL Server может проигнорировать этот индекс и выполнить сканирование всей таблицы (Index Scan или Table Scan). Это приведет к колоссальным задержкам на базах объемом в десятки гигабайт.

Чтобы заставить SQL Server использовать операцию быстрого поиска по индексу (Index Seek), мы обязаны добавить в условие фильтрации ограничение по виду документа IDDOCDEF:

AND Журнал.IDDOCDEF = $ВидДокумента.Перерасчет

Такое простое дополнение сокращает время выполнения запроса в десятки и сотни раз, так как СУБД сразу отсекает документы всех остальных видов еще на этапе сканирования индекса.

Шаг 4: Получение даты документа в результирующей выборке

Если вам требуется не просто отфильтровать документы, но и вывести дату каждого документа в результирующую таблицу значений в формате стандартной даты СУБД, используем строковые функции SQL. Мы можем "вырезать" первые 8 символов из поля DATE_TIME_IDDOC и привести их к типу DateTime:

CAST(LEFT(Журнал.DATE_TIME_IDDOC, 8) AS DateTime) AS [ДатаДокумента]

Этот прием позволяет получить полноценную дату на стороне SQL-сервера, которую можно использовать для сортировки, группировки или последующей математической обработки.

Полный практический пример реализации

Объединим все рассмотренные шаги, правила оптимизации и лучшие практики в один готовый рабочий скрипт для 1С 7.7. Мы также добавим хинт оптимизации WITH (NOLOCK), который предотвращает избыточные блокировки таблиц базы данных при чтении данных активными пользователями.


// Создаем объект для выполнения прямого запроса
Запрос = СоздатьОбъект("ODBCRecordset");
Запрос.УстБД1С();

// Формируем текст запроса с учетом оптимизации
ТекстЗапроса = "
|SELECT 
|   Журнал.IDDOC [Документ $Документ.Перерасчет],
|   $Перерасчет.ЛицевойСчет [ЛицевойСчет $Справочник.ЛицевыеСчета],
|   CAST(LEFT(Журнал.DATE_TIME_IDDOC, 8) AS DateTime) AS [ДатаДокумента]
|FROM 
|   $Документ.Перерасчет AS Перерасчет WITH (NOLOCK)
|INNER JOIN 
|   _1SJOURN AS Журнал WITH (NOLOCK) ON Перерасчет.IDDOC = Журнал.IDDOC
|WHERE 
|   Журнал.DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~
|   AND Журнал.IDDOCDEF = $ВидДокумента.Перерасчет
|";

// Устанавливаем параметры дат
Запрос.УстановитьТекстовыйПараметр("НачДата", ВыбНачДата);
Запрос.УстановитьТекстовыйПараметр("КонДата", ВыбКонДата);

// Выполняем запрос и выгружаем результат в Таблицу Значений
ТаблицаРезультатов = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);

// Теперь ТаблицаРезультатов содержит типизированные колонки, 
// готовые к стандартному обходу на стороне 1С
ТаблицаРезультатов.ВыбратьСтроки();
Пока ТаблицаРезультатов.ПолучитьСтроку() = 1 Цикл
    Сообщить("Документ: " + ТаблицаРезультатов.Документ + " от " + ТаблицаРезультатов.ДатаДокумента);
КонецЦикла;

Резюме

Для корректного и быстрого выбора документов по дате в прямых SQL-запросах 1С++ всегда придерживайтесь следующих правил:

  1. Всегда связывайте таблицу документа с таблицей общего журнала _1SJOURN по полю IDDOC.
  2. Для фильтрации по дате используйте поле DATE_TIME_IDDOC и модификатор параметра даты с тильдой (:КонДата~), чтобы не потерять документы, проведенные в последний день периода.
  3. Обязательно добавляйте условие по полю IDDOCDEF с использованием метаконструкции $ВидДокумента.ИмяДокумента для задействования кластерного индекса СУБД.
  4. Используйте хинты WITH (NOLOCK) для предотвращения транзакционных блокировок в многопользовательской среде.
← На главную