При разработке сложных отчетов или обработок по контролю уникальности серий в 1С:ERP, Комплексной автоматизации или Управлении торговлей 11, программисты часто сталкиваются с ситуацией, когда запрос к табличной части документа не возвращает данных, хотя визуально серии в документе присутствуют. Разберем по шагам, почему это происходит, как оптимизировать условия в запросах и как правильно организовать поиск дублей, чтобы система работала быстро и надежно.
Рассмотрим ситуацию, описанную автором: запрос к табличной части Серии документа ПриобретениеТоваровУслуг возвращает пустой результат, несмотря на наличие записей в регистре накопления ДвиженияСерийТоваров. Это классическая архитектурная особенность современных конфигураций 1С на базе платформы 8.3.
Проанализируем механизмы хранения серий. В 1С:ERP заполнение ТЧ Серии напрямую зависит от настройки «Политики учета серий». Существует несколько вариантов поведения системы:
Важный вывод: Для надежной проверки дублей всегда следует обращаться к регистру накопления ДвиженияСерийТоваров, так как именно он является «первоисточником» фактического движения серий в системе.
Посмотрим на пример кода из исходного сообщения. Использование множественных конструкций ИЛИ (OR) для проверки типов документов и значений перечислений — это плохая практика, которая затрудняет чтение кода и замедляет работу SQL-сервера. Разберем, как это можно улучшить.
Вместо перечисления:
ГДЕ (ДвиженияСерийТоваров.СкладскаяОперация = Значение(Перечисление.СкладскиеОперации.ПроизводствоПродукции)
ИЛИ ДвиженияСерийТоваров.СкладскаяОперация = Значение(Перечисление.СкладскиеОперации.ПриемкаОтПоставщика)
ИЛИ ...)
Следует использовать конструкцию В (...). Рассмотрим подробнее исправленный вариант:
ГДЕ ДвиженияСерийТоваров.СкладскаяОперация В (
ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПроизводствоПродукции),
ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаОтПоставщика),
ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ВводОстатков),
ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаПродукцииИзПроизводства)
)
Такой подход позволяет платформе 1С сформировать более эффективный SQL-запрос IN (...), который оптимизатор базы данных обрабатывает значительно быстрее.
Аналогичная проблема возникает при проверке типов ссылок через ССЫЛКА. Когда мы пишем Документ ССЫЛКА Документ.ПриобретениеТоваровУслуг, SQL-сервер зачастую вынужден выполнять LEFT JOIN ко всем таблицам документов, указанным в условии, что сильно нагружает систему. Проанализируем более изящный способ через ТИПЗНАЧЕНИЯ.
Рекомендуемый метод проверки группы типов:
ГДЕ ТИПЗНАЧЕНИЯ(ДвиженияСерийТоваров.Документ) В (
ТИП(Документ.ЭтапПроизводства2_2),
ТИП(Документ.ПроизводствоБезЗаказа),
ТИП(Документ.ВводОстатковТоваров),
ТИП(Документ.ОприходованиеИзлишковТоваров),
ТИП(Документ.ПриобретениеТоваровУслуг)
)
Конструкция ТИПЗНАЧЕНИЯ в запросе обращается к внутреннему полю идентификатора типа (в терминах SQL это обычно _Description_TRef), что работает на порядок быстрее, чем проверка каждой таблицы через ССЫЛКА.
Автор исходного сообщения использовал конструкцию ГДЕ Серия В (ВЫБРАТЬ ...). Это использование вложенного запроса в условии ГДЕ. Посмотрим, почему это опасно: для каждой строки из основной таблицы базы данных будет заново выполняться подзапрос. На больших объемах данных (а регистр ДвиженияСерийТоваров быстро растет) это приведет к зависанию системы.
Рассмотрим правильный алгоритм решения задачи:
Серия и Номенклатура.ВНУТРЕННЕЕ СОЕДИНЕНИЕ.Важный нюанс: Сама по себе Серия в 1С не уникальна. Условный серийный номер «10001» может быть у «Чайника» и у «Перфоратора». Чтобы найти настоящие дубли, всегда нужно связывать таблицы по паре Номенклатура + Серия.
Разберем пример запроса, который находит все серии из текущего документа, которые уже встречались в других документах системы:
// 1. Помещаем серии текущего объекта во временную таблицу
ВЫБРАТЬ
ТЧ.Номенклатура КАК Номенклатура,
ТЧ.Серия КАК Серия
ПОМЕСТИТЬ ВТ_СерииОбъекта
ИЗ
Документ.ПриобретениеТоваровУслуг.Товары КАК ТЧ // Или работаем через ДвиженияСерийТоваров
ГДЕ
ТЧ.Ссылка = &ТекущийОбъект
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
Серия
;
// 2. Ищем пересечения с регистром
ВЫБРАТЬ
Регистр.Документ КАК ДокументДубль,
ВТ.Серия КАК Серия,
ВТ.Номенклатура КАК Номенклатура
ИЗ
ВТ_СерииОбъекта КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ДвиженияСерийТоваров КАК Регистр
ПО ВТ.Номенклатура = Регистр.Номенклатура
И ВТ.Серия = Регистр.Серия
ГДЕ
Регистр.Документ <> &ТекущийОбъект
И ТИПЗНАЧЕНИЯ(Регистр.Документ) В (ТИП(Документ.ПриобретениеТоваровУслуг), ТИП(Документ.ЭтапПроизводства2_2))
Использование ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо В (ВЫБРАТЬ...) гарантирует, что SQL-сервер применит оптимальный план выполнения запроса. А индексация временной таблицы сделает поиск мгновенным даже при миллионах записей в регистре.
Подведем итог. Если ваш запрос «не видит» данные, проверьте, в том ли месте вы их ищете (ТЧ против Регистра). При написании условий откажитесь от бесконечных ИЛИ в пользу оператора В и используйте ТИПЗНАЧЕНИЯ. Эти простые правила не только решат проблему пустых результатов, но и сделают ваш код профессиональным и быстродействующим.