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