Как найти новые адреса доставки, которых не было в прошлых периодах, через запрос 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

Часто перед разработчиком 1С встает задача: получить список уникальных значений за текущий период, которых гарантированно не было в базе за предыдущие месяцы или годы. Например, это могут быть новые адреса доставки, новые контрагенты или уникальные серийные номера. Попытка решить эту задачу «в лоб» через условие НЕ В (ВЫБРАТЬ...) часто приводит к тому, что запрос либо работает крайне медленно, либо со временем перестает возвращать данные вовсе. Разберем, почему это происходит, и как построить надежный и быстрый запрос.

Почему исходный запрос с «НЕ В» может не работать?

Проанализируем ситуацию, описанную в проблеме. У автора был запрос, который проверял наличие адреса в подзапросе за прошлые 6 месяцев. Основная причина внезапного «отказа» такого кода кроется в логике работы оператора НЕ В (в SQL это NOT IN). Рассмотрим ключевой нюанс: если подзапрос вернет хотя бы одну строку со значением NULL, то весь результат условия НЕ В станет неопределенным. Система просто не сможет сопоставить данные, и основной запрос вернет пустой результат. С ростом базы данных вероятность появления NULL (например, из-за незаполненного реквизита адреса в одном из документов) стремится к 100%.

Второй важный момент — производительность. Использование конструкции ВЫРАЗИТЬ(Поле КАК СТРОКА) внутри условий ГДЕ делает запрос «неаргументопригодным» (non-SARGable). Это означает, что СУБД не может использовать индексы для поиска, и каждый раз будет выполнять полное сканирование таблиц, что на больших периодах (6 месяцев) приведет к катастрофическому падению скорости.

Решение через временные таблицы и левое соединение

Самым правильным и надежным способом решения данной задачи в 1С является использование механизма временных таблиц и левого соединения. Разберем этот подход по шагам.

Шаг 1: Получаем список адресов за прошлые периоды.

Сначала мы соберем все адреса, которые уже встречались в системе за последние 6 месяцев, и поместим их в первую временную таблицу. Назовем её ВТ_АдресаЗаПрошлыйПериод.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫРАЗИТЬ(ПродажиОбороты.ДокументПродажи.АдресДоставки КАК СТРОКА(200)) КАК Адрес
ПОМЕСТИТЬ ВТ_АдресаЗаПрошлыйПериод
ИЗ
    РегистрНакопления.Продажи.Обороты(
        НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Период, МЕСЯЦ, -6), МЕСЯЦ), 
        КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&Период, МЕСЯЦ, -1), МЕСЯЦ), , ) КАК ПродажиОбороты
ГДЕ
    НЕ ВЫРАЗИТЬ(ПродажиОбороты.ДокументПродажи.АдресДоставки КАК СТРОКА(200)) ЕСТЬ NULL

ИНДЕКСИРОВАТЬ ПО
    Адрес
;

Обратим внимание: мы добавили условие ЕСТЬ NULL, чтобы исключить пустые значения, и обязательно добавили ИНДЕКСИРОВАТЬ ПО. Индексация временной таблицы критически важна для быстрого соединения во втором пакете.

Шаг 2: Получаем текущие данные и ищем исключения.

Теперь выберем данные за текущий месяц и соединим их с нашей временной таблицей через ЛЕВОЕ СОЕДИНЕНИЕ. Те адреса, которых нет в первой таблице, после соединения будут иметь значение NULL.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫРАЗИТЬ(ПродажиОборотыТек.ДокументПродажи.АдресДоставки КАК СТРОКА(200)) КАК АдресДоставки,
    ПродажиОборотыТек.Проект.Менеджер КАК ПроектМенеджер
ИЗ
    РегистрНакопления.Продажи.Обороты(НАЧАЛОПЕРИОДА(&Период, МЕСЯЦ), КОНЕЦПЕРИОДА(&Период, ДЕНЬ), , ) КАК ПродажиОборотыТек
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_АдресаЗаПрошлыйПериод КАК ВТ_Прошлое
    ПО (ВЫРАЗИТЬ(ПродажиОборотыТек.ДокументПродажи.АдресДоставки КАК СТРОКА(200)) = ВТ_Прошлое.Адрес)
ГДЕ
    ВТ_Прошлое.Адрес ЕСТЬ NULL

Почему этот метод лучше?

Проанализируем преимущества такого подхода:

  1. Стабильность результата: Наличие NULL в данных больше не «ломает» логику запроса. Условие ЕСТЬ NULL четко отсекает те записи, по которым не нашлось соответствия в прошлом периоде.
  2. Производительность: СУБД гораздо эффективнее обрабатывает левое соединение проиндексированных таблиц, чем вложенные подзапросы с отрицанием.
  3. Читаемость: Запрос разделен на логические блоки. Мы сначала готовим данные, а потом их анализируем, что упрощает отладку.

Дополнительные рекомендации по оптимизации

Выясним еще несколько важных моментов, которые помогут сделать работу с адресами более эффективной:

Работа со строками: Помните, что ВЫРАЗИТЬ(... КАК СТРОКА(200)) — это вынужденная мера для реквизитов неограниченной длины. Если в вашей конфигурации есть возможность хранить нормализованный адрес или его уникальный идентификатор (например, из ФИАС/ГАР), лучше использовать именно его. Сравнение строк — самая тяжелая операция для сервера.

Использование виртуальных таблиц: Мы используем таблицу Обороты. Если вам нужно анализировать только факт наличия адреса, убедитесь, что в параметрах виртуальной таблицы не задана лишняя детализация (например, по регистратору), если она не нужна для получения адреса. Это существенно ускорит выборку.

Очистка данных: При сравнении строк 1С учитывает пробелы. Рассмотрим ситуацию: если в прошлом месяце адрес был записан как "г. Москва ", а в текущем как "г. Москва", система посчитает их разными. Для более точного сопоставления рекомендуется на уровне записи данных очищать строки от лишних пробелов функцией СокрЛП().

Таким образом, замена НЕ В на связку «Временная таблица + Левое соединение + Проверка на NULL» превращает нестабильный запрос в профессиональный инструмент, готовый к работе с большими объемами данных.

← На главную