При разработке интерфейсов в 1С часто возникает задача: отобразить на форме список элементов справочника (например, контрагентов или организаций) и показать их текущие настройки из регистра сведений. При этом, если настроек в регистре для конкретного элемента еще нет, программа должна выводить понятный пользователю статус (например, «НЕ настроен»), а если запись присутствует — показывать статус «Настроено» или конкретные параметры.
Начинающие разработчики часто совершают архитектурные и логические ошибки на этом пути: пытаются считывать наборы записей при открытии формы, выполнять лишние транзакции записи или некорректно связывают таблицы в запросах. В этой статье мы подробно разберем, как решить данную задачу правильно, оптимально с точки зрения производительности СУБД и красиво с точки зрения архитектуры 1С.
Для начала проанализируем, почему код автора из первого сообщения темы не работал или работал крайне неэффективно. Типичный ошибочный подход выглядит следующим образом:
// Ошибочный подход чтения всего регистра через набор записей
НаборЗаписей = РегистрыСведений.НастройкаВебСерверов.СоздатьНаборЗаписей;
НаборЗаписей.Прочитать();
ТзРег = НаборЗаписей.Выгрузить();
Для Каждого Регистр Из ТзРег Цикл
Если НЕ ЗначениеЗаполнено(Регистр.Организация) Тогда
НовСтрока = ТЗ.Добавить();
НовСтрока.Организация = Регистр.Организация;
КонецЕсли;
КонецЦикла;
Давайте выясним причины, по которым данный подход категорически неприменим в реальных задачах:
Прочитать() без установки отбора считывает абсолютно все записи из регистра сведений в оперативную память. Если в базе данных будут десятки тысяч записей, это приведет к зависанию формы и перегрузке сервера.ЗначениеЗаполнено(Регистр.Организация) внутри цикла по существующим записям регистра бессмысленна. Если записи по контрагенту в регистре нет, то цикл по таблице регистра вообще «не узнает» об этом контрагенте. Нам нужно отталкиваться от справочника контрагентов, а не от таблицы регистра.Для решения нашей задачи идеально подходит операция ЛЕВОГО СОЕДИНЕНИЯ (LEFT JOIN) в запросе. Мы берем за основу таблицу справочника Справочник.Контрагенты (левая таблица) и присоединяем к ней виртуальную таблицу среза последних регистра сведений РегистрСведений.НастройкаВебСерверов.СрезПоследних (правая таблица).
При левом соединении СУБД вернет все строки из левой таблицы. Если для какого-то контрагента в правой таблице (регистре сведений) найдется соответствующая запись, то поля регистра заполнятся реальными данными. Если же записи в регистре нет, то все поля из правой таблицы вернут специальное значение — NULL.
Важное предупреждение: Распространенной ошибкой является наложение условий на правую (присоединяемую) таблицу в секции ГДЕ. Рассмотрим этот пример подробнее:
// ТАК ДЕЛАТЬ НЕЛЬЗЯ! Левое соединение превратится во внутреннее
ВЫБРАТЬ
Контрагенты.Ссылка КАК Организация,
Настройки.ВебСервер КАК ВебСервер
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкаВебСерверов.СрезПоследних КАК Настройки
ПО Контрагенты.Ссылка = Настройки.Организация
ГДЕ
Настройки.Рабочая = ИСТИНА // ОШИБКА! Строки с NULL будут отброшены
Поскольку для отсутствующих в регистре организаций поле Настройки.Рабочая примет значение NULL, условие NULL = ИСТИНА вернет ложь. В результате СУБД просто отбросит все строки, для которых нет настроек, и левое соединение фактически превратится во внутреннее. Чтобы этого избежать, любые фильтры на присоединяемую таблицу нужно накладывать исключительно в параметрах самой виртуальной таблицы.
Для корректного написания условий в 1С крайне важно различать три вида «пустоты»:
NULL — это отсутствие значения как такового. Оно существует только на уровне СУБД и запросов 1С. Появляется в результате соединений таблиц. Его нельзя сравнивать через оператор равенства (Поле = NULL всегда вернет Ложь). Проверка осуществляется только конструкцией ЕСТЬ NULL.Неопределено — это тип данных встроенного языка 1С. Используется для обозначения незаполненных переменных или реквизитов составного типа в коде программы.ПустаяСсылка — это вполне реальный объект базы данных определенного типа (например, Справочники.Контрагенты.ПустаяСсылка()). У этого объекта есть свой уникальный идентификатор (UID, состоящий из нулей), но у него нет заполненных реквизитов в интерфейсе. Наличие пустой ссылки говорит о том, что поле заполнено, но указывает на пустой элемент.В запросах часто используют функцию ЕСТЬNULL(Поле, ЗначениеЗамены). Например: ЕСТЬNULL(Настройки.ВебСервер, "НЕ настроен"). Однако у этой функции есть важная особенность: тип возвращаемого значения приводится к типу первого аргумента.
Если в конфигураторе для ресурса ВебСервер задана длина строки, например, 10 символов, то строка замены "НЕ настроен" (длина которой 11 символов) будет безжалостно обрезана до 10 символов (получится "НЕ настрое"). Для текстовых статусов гораздо безопаснее и гибче использовать конструкцию ВЫБОР, которая динамически определяет наиболее подходящий тип:
ВЫБОР
КОГДА Настройки.Организация ЕСТЬ NULL ТОГДА "НЕ настроен"
ИНАЧЕ "Настроено"
КОНЕЦ КАК СтатусНастройки
Рассмотрим по шагам реализацию этой задачи для обычной формы, где на форме размещена таблица значений ТаблицаНаФорме с колонками Организация и ВебСервер.
Шаг 1. Создание и настройка запроса
Нам необходимо написать оптимальный запрос, который выберет контрагентов из определенной группы (папки) и соединит их с регистром сведений. При этом параметры отбора для регистра мы передадим непосредственно в параметры виртуальной таблицы СрезПоследних.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Организация,
| ВЫБОР
| КОГДА Настройки.Организация ЕСТЬ NULL ТОГДА ""НЕ настроен""
| ИНАЧЕ Настройки.ВебСервер
| КОНЕЦ КАК ВебСервер
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкаВебСерверов.СрезПоследних(
| ,
| Организация В ИЕРАРХИИ(&ЮрЛица)
| ) КАК Настройки
| ПО Контрагенты.Ссылка = Настройки.Организация
|ГДЕ
| Контрагенты.Ссылка В ИЕРАРХИИ(&ЮрЛица)
| И НЕ Контрагенты.ЭтоГруппа";
Запрос.УстановитьParameter("ЮрЛица", Справочники.Контрагенты.НайтиПоКоду("У00000127"));
Шаг 2. Заполнение таблицы на форме
После выполнения запроса мы обходим выборку в цикле и заполняем строки нашей таблицы значений на форме. Для быстрого копирования данных из выборки в строку таблицы идеально подходит метод встроенного языка ЗаполнитьЗначенияСвойств.
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Очищаем старые данные на форме перед заполнением
ТаблицаНаФорме.Очистить();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТаблицаНаФорме.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
КонецЦикла;
Иногда перед разработчиком ставится альтернативная задача: вывести на форму исключительно тех контрагентов, по которым вообще нет записей в регистре сведений (чтобы пользователь сразу видел, кого нужно настроить). Такой подход в базах данных называется Anti-Join.
Для этого нам достаточно модифицировать секцию ГДЕ нашего запроса, проверяя ключевое поле присоединенной таблицы на NULL:
ЗапросНенастроенные = Новый Запрос;
ЗапросНенастроенные.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Организация,
| ""НЕ настроен"" КАК ВебСервер
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкаВебСерверов.СрезПоследних(
| ,
| Организация В ИЕРАРХИИ(&ЮрЛица)
| ) КАК Настройки
| ПО Контрагенты.Ссылка = Настройки.Организация
|ГДЕ
| Контрагенты.Ссылка В ИЕРАРХИИ(&ЮрЛица)
| И НЕ Контрагенты.ЭтоГруппа
| И Настройки.Организация ЕСТЬ NULL"; // Выбираем только те строки, которых нет в регистре
ЗапросНенастроенные.УстановитьПараметр("ЮрЛица", Справочники.Контрагенты.НайтиПоКоду("У00000127"));
Вывод жестко прописанной строки "НЕ настроен" прямо из текста запроса — рабочий, но не самый гибкий вариант. В профессиональной разработке принято разделять данные и их визуальное представление.
Гораздо правильнее возвращать из запроса булевый флаг настройки (например, ИСТИНА/ЛОЖЬ), а красивое текстовое представление или подсветку цветом делать на уровне интерфейса:
ВЫБОР КОГДА Настройки.Организация ЕСТЬ NULL ТОГДА ЛОЖЬ ИНАЧЕ ИСТИНА КОНЕЦ КАК Настроено.ПриВыводеСтроки табличного поля. В нем можно закрасить ненастроенные строки красным цветом или вывести пиктограмму статуса.