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

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие 7.7 часто возникает задача анализа «возраста» товара. Одним из ключевых показателей является дата самого первого поступления позиции на склад. Если список товаров велик, использование штатных средств (объекта Запрос или перебора выборки) может быть крайне медленным. В таких случаях на помощь приходят прямые SQL-запросы.

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

Постановка задачи и выбор стратегии

Нам необходимо получить для каждой позиции из переданного списка (например, загруженного во временную таблицу #tmptable) дату самого раннего документа прихода. Рассмотрим два основных подхода:

  1. Поиск по табличным частям документов: соединение таблицы строк конкретных документов (например, «Поступление товаров») с журналом документов.
  2. Поиск по регистрам накопления: анализ движений регистра «ОстаткиТМЦ» (или аналогичного), где фиксируются приходы.

Проанализируем технические нюансы. В 1С 7.7 дата и время документа, а также его уникальный идентификатор (IDDOC) хранятся в объединенном поле DATE_TIME_IDDOC. Оно имеет длину 23 символа, где первые 8 символов — это дата в формате YYYYMMDD. Использование функции MIN() или TOP 1 с сортировкой по этому полю позволяет найти не просто самый ранний день, но и самый первый документ в рамках этого дня.

Решение через соединение с журналом документов и табличной частью

Если нам нужно найти дату поступления строго по определенному виду документа (например, только «ПоступлениеТоваров»), мы можем использовать коррелированный подзапрос. Рассмотрим пример кода, который можно использовать в 1С++:


SELECT
    -- Получаем дату из поля Date_Time_IDDOC (первые 8 символов)
    (SELECT TOP 1 LEFT(Жур.Date_Time_IDDOC, 8) 
     FROM _1sjourn AS Жур (NOLOCK) 
     INNER JOIN $ДокументСтроки.ПоступлениеТоваров AS Док (NOLOCK) ON Док.iddoc = Жур.iddoc 
     WHERE $Док.Номенклатура = Спр.id 
       AND Жур.iddocdef = $ВидДокумента.ПоступлениеТоваров
     ORDER BY Жур.Date_Time_IDDOC ASC) AS [ДатаПервогоПоступления $Дата],
    Спр.id AS [Номенклатура $Справочник.Номенклатура]
FROM $Справочник.Номенклатура AS Спр (NOLOCK) 
WHERE Спр.id IN (SELECT val FROM #tmptable)

Важное замечание: этот метод может работать медленно на больших объемах данных, так как подзапрос выполняется для каждой строки основной таблицы. Если список номенклатуры большой, рекомендуется использовать LEFT JOIN с предварительно сгруппированными данными.

Оптимизированное решение через регистры накопления

Для более универсального поиска (учитывающего любые приходы, а не только один вид документа) лучше использовать таблицу движений регистров (таблицы с префиксом RA). Это особенно эффективно, если на измерении Номенклатура установлен флаг «Быстрый отбор».

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

Рассмотрим пример запроса к регистру «ОстаткиТМЦ»:


SELECT 
    val AS [Номенклатура $Справочник.Номенклатура],
    (SELECT TOP 1 LEFT(Рег.Date_Time_IDDOC, 8) 
     FROM $Регистр.ОстаткиТМЦ AS Рег (NOLOCK) 
     WHERE $Рег.Номенклатура = #tmptable.val 
       -- Фильтруем только приходные движения, если это необходимо
       -- В 1С 7.7 приход в RA таблице обычно имеет флаг DEBKR = 0
       AND Рег.debkr = 0 
     ORDER BY Рег.Date_Time_IDDOC ASC) AS [Дата $Дата]
FROM #tmptable

В данном примере мы обращаемся напрямую к временной таблице #tmptable, что исключает лишнее соединение со справочником номенклатуры, если нам нужны только идентификаторы объектов.

Проблема производительности и индексы

При выполнении таких запросов критически важно наличие индексов. Если в конфигураторе для измерения «Номенклатура» регистра «ОстаткиТМЦ» не стоит галочка «Быстрый отбор», SQL-сервер будет вынужден выполнять полное сканирование таблицы (Index Scan или Table Scan). На больших базах это приведет к зависанию отчета.

Посмотрим на рекомендации по оптимизации:

  1. Проверьте индексы: Если задача поиска первого/последнего прихода выполняется часто, убедитесь, что «Быстрый отбор» включен.
  2. Используйте агрегатные функции: Вместо TOP 1 с подзапросом в ряде случаев быстрее работает GROUP BY с функцией MIN().
  3. Ограничьте период: Если вы точно знаете, что товар не мог поступить раньше определенного года (например, года запуска системы), добавьте условие по Date_Time_IDDOC, чтобы ограничить диапазон сканирования индекса.

Итоговые рекомендации

Мы проанализировали ситуацию и выяснили, что наиболее надежным и быстрым способом для 1С 7.7 является прямой SQL-запрос к таблице движений регистра накопления. Если вы используете 1С++, не забывайте типизировать возвращаемые значения (например, через конструкцию [Дата $Дата]), чтобы платформа корректно преобразовала строку SQL в объект даты 1С.

Разберем еще один вариант, когда нужно найти дату первого поступления вообще по любому документу, который сделал движения «Приход» по регистру, без привязки к виду документа:


SELECT 
    $Рег.Номенклатура AS [Номенклатура $Справочник.Номенклатура],
    MIN(LEFT(Рег.Date_Time_IDDOC, 8)) AS [ДатаПервогоПрихода $Дата]
FROM $Регистр.ОстаткиТМЦ AS Рег (NOLOCK)
WHERE $Рег.Номенклатура IN (SELECT val FROM #tmptable)
  AND Рег.debkr = 0 -- Только приход
GROUP BY $Рег.Номенклатура

Этот подход с GROUP BY зачастую оказывается эффективнее коррелированных подзапросов, так как SQL-сервер может выполнить соединение таблиц через Hash Match, что значительно быстрее для больших списков товаров.

← На главную