Часто перед разработчиком 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
Проанализируем преимущества такого подхода:
NULL в данных больше не «ломает» логику запроса. Условие ЕСТЬ NULL четко отсекает те записи, по которым не нашлось соответствия в прошлом периоде.Выясним еще несколько важных моментов, которые помогут сделать работу с адресами более эффективной:
Работа со строками: Помните, что ВЫРАЗИТЬ(... КАК СТРОКА(200)) — это вынужденная мера для реквизитов неограниченной длины. Если в вашей конфигурации есть возможность хранить нормализованный адрес или его уникальный идентификатор (например, из ФИАС/ГАР), лучше использовать именно его. Сравнение строк — самая тяжелая операция для сервера.
Использование виртуальных таблиц: Мы используем таблицу Обороты. Если вам нужно анализировать только факт наличия адреса, убедитесь, что в параметрах виртуальной таблицы не задана лишняя детализация (например, по регистратору), если она не нужна для получения адреса. Это существенно ускорит выборку.
Очистка данных: При сравнении строк 1С учитывает пробелы. Рассмотрим ситуацию: если в прошлом месяце адрес был записан как "г. Москва ", а в текущем как "г. Москва", система посчитает их разными. Для более точного сопоставления рекомендуется на уровне записи данных очищать строки от лишних пробелов функцией СокрЛП().
Таким образом, замена НЕ В на связку «Временная таблица + Левое соединение + Проверка на NULL» превращает нестабильный запрос в профессиональный инструмент, готовый к работе с большими объемами данных.