При разработке и поддержке конфигураций на платформе 1С:Предприятие 7.7 одной из самых частых задач является выборка элементов справочника по значению конкретного реквизита. Для этого в платформе существует стандартный и очень удобный метод ВыбратьЭлементыПоРеквизиту(). Однако начинающие разработчики (а также программисты, переходящие на «семерку» с платформы 1С 8.x) регулярно сталкиваются с ситуацией, когда этот метод упорно возвращает 0 (выборка пуста), хотя в базе точно присутствуют подходящие элементы.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем типичный ошибочный код, рассмотрим правильное решение и изучим все скрытые технические нюансы работы с выборками по реквизитам в среде 1С 7.7.
Рассмотрим ситуацию, с которой часто сталкиваются разработчики. Нам необходимо выбрать элементы подчиненного справочника БП_ОбъектыНаОбслуживании по значению реквизита ВидОбъекта, который является ссылкой на другой справочник — БП_ВидыОбслуживаемыхОбъектов.
Посмотрим на исходный код, который не приносил ожидаемого результата и возвращал пустую выборку:
СпрОбъекты = СоздатьОбъект("Справочник.БП_ОбъектыНаОбслуживании");
СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент());
СправочникВидов = СоздатьОбъект("Справочник.БП_ВидыОбслуживаемыхОбъектов");
РеквизитПоиска = СправочникВидов.НайтиПоНаименованию("Банковское оборудование");
СпрОбъекты.ВыбратьЭлементыПоРеквизиту("ВидОбъекта", РеквизитПоиска, 0, 0);
Пока СпрОбъекты.ПолучитьЭлемент() = 1 Цикл
// Обработка элементов...
КонецЦикла;
На первый взгляд, логика построена абсолютно верно: мы инициализируем объекты, находим вид объекта по наименованию, передаем полученную переменную в качестве параметра поиска в целевой справочник и обходим результаты в цикле. Тем не менее этот код не находит ни одного элемента. Выясним, почему так происходит.
Главная ошибка кроется в строке получения значения для отбора: РеквизитПоиска = СправочникВидов.НайтиПоНаименованию("Банковское оборудование");.
В платформе 1С 7.7 метод НайтиПоНаименованию() у объекта справочника не возвращает ссылку на найденный элемент. Он выполняет позиционирование самого объекта справочника и возвращает **число** (флаг успешности операции):
Таким образом, переменная РеквизитПоиска получает значение 1 (или 0), а вовсе не ссылку на элемент справочника видов обслуживания! В результате метод ВыбратьЭлементыПоРеквизиту() пытается выполнить поиск по реквизиту ВидОбъекта, сравнивая его значение с числом 1. Естественно, такое сравнение не дает результатов, и выборка оказывается пустой.
Кроме того, в исходном коде допущена серьезная методическая ошибка: отсутствует проверка успешности поиска. Если нужного вида оборудования не окажется в базе данных (например, его переименовали или удалили), код продолжит работу со значением 0, не сообщая пользователю о проблеме.
Чтобы исправить эту ошибку, нам необходимо получить ссылку на сам найденный элемент базы данных. Для этого используется свойство ТекущийЭлемент() позиционированного объекта справочника. Проанализируем правильный и надежный вариант реализации этого алгоритма:
// Шаг 1. Инициализируем справочник видов обслуживания
СправочникВидов = СоздатьОбъект("Справочник.БП_ВидыОбслуживаемыхОбъектов");
// Шаг 2. Выполняем поиск и проверяем его успешность
Если СправочникВидов.НайтиПоНаименованию("Банковское оборудование") = 0 Тогда
Предупреждение("Внимание! Не найден вид обслуживания 'Банковское оборудование'.");
Возврат;
КонецЕсли;
// Шаг 3. Получаем реальную ссылку на найденный элемент справочника
РеквизитПоиска = СправочникВидов.ТекущийЭлемент();
// Шаг 4. Инициализируем целевой справочник объектов на обслуживании
СпрОбъекты = СоздатьОбъект("Справочник.БП_ОбъектыНаОбслуживании");
// Шаг 5. При необходимости устанавливаем фильтр по владельцу
СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент());
// Шаг 6. Выполняем выборку по значению реквизита
СпрОбъекты.ВыбратьЭлементыПоРеквизиту("ВидОбъекта", РеквизитПоиска, 0, 0);
// Шаг 7. Обходим результаты выборки в цикле
Пока СпрОбъекты.ПолучитьЭлемент() = 1 Цикл
// Теперь выборка работает корректно!
// Обрабатываем данные, например, добавляем в таблицу значений:
ТабОбъектыБанк.НоваяСтрока();
ТабОбъектыБанк.Объект = СпрОбъекты.ТекущийЭлемент();
ТабОбъектыБанк.СерийныйНомер = СпрОбъекты.СерийныйНомер.ЗаводскойНомер;
КонецЦикла;
В кодовой базе автора темы используется метод ограничения выборки по владельцу: СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент());. Этот механизм отлично поддерживается платформой при работе с выборками, но важно помнить два критически важных правила:
ИспользоватьВладельца() должен вызываться строго ДО вызова метода ВыбратьЭлементыПоРеквизиту(). Если вызвать его после открытия выборки, фильтрация по владельцу не применится, либо выборка будет сброшена.СпрОбъекты.ИспользоватьВладельца("");.Даже если ваш программный код написан идеально, попытка вызвать метод ВыбратьЭлементыПоРеквизиту() может привести к аварийному завершению работы программы (Runtime Error). Чтобы этого избежать, необходимо удостовериться, что структура метаданных в Конфигураторе настроена корректно.
Для реквизита справочника, по которому планируется выполнять отбор, в конфигураторе обязательно должен быть установлен флаг «Сортировка». На уровне базы данных (как в DBF, так и в SQL-версиях) это создает индекс по данному полю. Без этого индекса платформа физически не сможет выполнить быстрый отбор, поэтому встроенный язык 1С 7.7 заблокирует выполнение метода.
Также важно помнить о следующих принципиальных ограничениях платформы 1С 7.7:
1SCONST, а не в основной таблице справочника).Давайте подробнее проанализируем синтаксис и назначение всех параметров этого метода:
ВыбратьЭлементыПоРеквизиту(ИмяРеквизита, Значение, РежимИерархии, РежимГрупп)
"ВидОбъекта").1 (значение по умолчанию) — элементы выбираются с учетом иерархии (в порядке подчинения группам справочника).0 — элементы выбираются «плоским» списком, без учета того, в каких группах они находятся. Использование значения 0 существенно ускоряет выполнение запроса на больших объемах данных.1 — выборка будет производиться в том числе и среди групп (папок) справочника, если у них заполнен искомый реквизит.0 (значение по умолчанию) — выборка строится исключительно по конечным элементам справочника, группы в результат не попадают.Если бы реквизит ВидОбъекта имел строковый (текстовый) тип данных, вы могли бы столкнуться со специфической проблемой концевых пробелов, характерной для файловых баз данных DBF.
В базах данных формата DBF все строковые поля имеют строго фиксированную длину, заданную в метаданных конфигуратора. Например, если длина реквизита установлена в 25 символов, а вы программно записали туда строку "Банк" (4 символа), СУБД автоматически дополнит её 21 пробелом справа. При поиске через ВыбратьЭлементыПоРеквизиту() или НайтиПоРеквизиту() платформа 1С 7.7 выполняет точное посимвольное сравнение. Если вы передадите в качестве параметра поиска обычную строку "Банк", выборка вернет 0 (элементы не найдены).
Чтобы избежать этой проблемы при работе со строковыми реквизитами, используйте один из следующих проверенных подходов:
Способ 1: Программное дополнение строки поиска пробелами
ИскомоеЗначение = "Банк";
МаксДлинаРеквизита = 25; // Длина реквизита в конфигураторе
Пока СтрДлина(ИскомоеЗначение) < МаксДлинаРеквизита Цикл
ИскомоеЗначение = ИскомоеЗначение + " ";
КонецЦикла;
// Теперь строка поиска имеет точную длину и отбор сработает корректно
Спр.ВыбратьЭлементыПоРеквизиту("СтроковыйРеквизит", ИскомоеЗначение, 0, 0);
Способ 2: Корректная обработка данных при записи
При заполнении реквизитов всегда используйте функцию СокрЛП(), чтобы очистить данные от случайных пробелов, и следите за соответствием типов и длины данных.
Часто разработчики путают назначение этих встроенных методов работы со справочниками. Разберем принципиальное различие между ними:
НайтиПоРеквизиту() — используется для быстрого позиционирования на **первом** найденном элементе справочника. Этот метод возвращает 1 или 0. С его помощью невозможно обойти группу элементов в цикле, так как повторный вызов метода всегда будет позиционировать объект на одном и том же первом найденном элементе.ВыбратьЭлементыПоРеквизиту() — открывает полноценный курсор выборки (набор данных) для последующего последовательного обхода всех найденных записей с помощью метода ПолучитьЭлемент().Стандартный метод ВыбратьЭлементыПоРеквизиту() имеет еще одно жесткое ограничение: он позволяет делать отбор только по **одному** реквизиту. Если перед вами стоит задача выбрать элементы справочника одновременно по двум и более условиям (например, вид объекта равен «Банковское оборудование» и признак «НаОбслуживании» равен 1), этот метод не поможет.
В профессиональной разработке на 1С 7.7 для решения таких задач применяются альтернативные подходы:
1С++ или 1SQLite) для выполнения прямых SQL-запросов к таблицам базы данных в обход штатных механизмов платформы.Мы детально проанализировали механизм отборов в справочниках 1С 7.7. Понимание разницы между успешностью поиска и ссылкой на текущий элемент, а также знание тонкостей настройки метаданных позволят вам писать быстрый, стабильный и эффективный программный код.