Злопу. Некоторые особенности использования функции .ВыбратьЭлементыПоРеквизиту()

Автор Харлампий Дымба, 21 августа 2024, 17:28

« назад - далее »

Харлампий Дымба

Функция ВыбратьЭлементыПоРеквизиту(<?>,,,)
Англоязычный синоним: SelectItemsByAttribute
Синтаксис: ВыбратьЭлементыПоРеквизиту(<ИмяРеквизита>,<Значение>,<РежимИерархии>,<РежимГрупп>)
Назначение: Открыть выборку элементов справочника по значению реквизита.
Возвращает: 1 - если действие выполнено и в выборке есть хотя бы один элемент;
0 - если действие не выполнено или в выборке нет ни одного элемента.
Параметры:
<ИмяРеквизита> - строка с именем реквизита, по которому выполняется выборка;
<Значение> - значение реквизита для выборки (обязателен, но можно не указывать, по умолчанию - "")
<РежимИерархии> - как выбирать:
1 - выбирать элементы с учетом иерархии,
0 - выбирать элементы без учета иерархии (необязателен, по умолчанию - 1);
<РежимГрупп> - что выбирать:
1 - выбирать среди групп справочника,
0 - выбирать только среди элементов справочника (необязателен, по умолчанию - 0).

Метод можно использовать только для реквизитов с установленным признаком "Свойство реквизита - Дополнительные - Сортировка'', иначе выдаёт ошибку "Неверное имя реквизита!".
 
Метод не может быть использован для реквизитов "Код" и "Наименование", иначе выдаёт ошибку "Неверное имя реквизита!".

Метод  можно использовать только для объектов "Справочник определенного вида", созданных функцией СоздатьОбъект, иначе выдает ошибку "Объект не может быть перепозиционирован!" (также см. багофичу 2)
 
Если у справочника не существует реквизита, указанного в параметре <ИмяРеквизита>, или параметр не указан при вызове функции, то выдает ошибку "Неверное имя реквизита!"

Для параметра <ИмяРеквизита> регистр не имеет значения:
.ВыбратьЭлементыПоРеквизиту("МОЙРЕКВИЗИТ","1234");
.ВыбратьЭлементыПоРеквизиту("МойРеквизит","1234");
дадут одинаковый результат.

Если параметр <Значение> не указан, то выборка идёт по пустой строке
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"");
дадут одинаковый результат

Если надо сделать выборку по пустому значению, то тип в параметре <Значение>  должен строго соответствовать типу реквизита < ИмяРеквизита >, заданному в конфигураторе:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,ПолучитьПустоеЗначение("Справочник.МойСправочник")); // для агрегатного типа данных определенного вида
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,ПолучитьПустоеЗначение("Справочник")); // для агрегатного типа данных неопределенного вида
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,ПолучитьПустоеЗначение("Дата")); // для базового тип данных
В противном случае, выборка может быть некорректной (багофича №4)

В параметре <Значение> можно передавать и объект, и ссылку:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,СпрДляПоиска);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,СпрДляПоиска.ТекущийЭлемент());
дадут одинаковый результат.

Для параметра <Значение> пробелы справа не имеют значения, пробелы слева имеют значение:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234  ");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234 ");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234");
дадут одинаковый результат.

Значение отбора, указанное в параметре <Значение>  обрезается по длине реквизита. Например, если ИмяРеквизита - Строка(4), то
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"12345"); // "12345" будет обрезано до "1234"
дадут одинаковый результат - отбор по "1234".

Как и во всех других функциях поиска и выборки по строковым реквизитам справочников и документов регистр параметра <Значение> не важен:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"АБВГ");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"абвг");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"АбвГ");
дадут одинаковый результат.

Значения, указанные в параметрах <РежимИерархии> и <РежимГрупп> приводятся по формуле РежимИерархии =Число(СокрЛП((РежимИерархии)) и РежимГрупп=Число(СокрЛП((РежимГрупп)).
Если РежимИерархии=1 или параметр <РежимИерархии> не указан, то элементы выбираются с учетом иерархии:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234"); // параметр не указан;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",ПолучитьПустоеЗначение()); // параметр не указан;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",1); // равно 1;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234"," 1 "); // равно 1;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234","1а"); // равно 1;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",'20.08.2024'-2460542); // равно 1.
дадут одинаковый результат.

Если Число(СокрЛП((РежимИерархии))<>1, то элементы выбираются без учета иерархии:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",0);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",ПолучитьПустоеЗначение("Дата"));
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",2);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234","12");
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",ТекущаяДата());
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234","");
дадут одинаковый результат.

Если параметр <РежимИерархии> не равен 1, то .ИспользоватьРодителя() и .ИспользоватьВладельца() игнорируются и выборка идёт по всему справочнику. При этом также полностью игнорируется параметр <РежимГрупп>. Выборка делается и среди групп, и среди элементов справочника:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",0,1)
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",0,0)
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"1234",0)
дадут одинаковый результат.

Если параметр <РежимИерархии> равен 1, то выборка (в отличие от использования обычного метода .ВыбратьЭлементы(1)) идет только по одному уровню справочника без вложенных элементов/групп, и выбрать можно либо только группы, либо только элементы этого уровня в зависимости от значения параметра <РежимГрупп>.

  • Если справочник подчиненный:
    Если параметр <РежимИерархии> равен 1, то необходимо предварительно задать Владельца-элемент в ведущем справочнике через .ИспользоватьВладельца(). Выборка будет сделана только по одному уровню, без вложенных элементов, - верхнему или заданному через .ИспользоватьРодителя(). Иначе выборка будет пустая.
  • Если справочник многоуровневый:
    Если параметр <РежимИерархии> равен 1, то можно предварительно задать Родителя-группу через .ИспользоватьРодителя(). Выборка будет сделана только по одному уровню, без вложенных элементов, - верхнему или заданному через .ИспользоватьРодителя(). Иначе выборка будет пустая.
  • Если справочник подчиненный многоуровневый:
    Если параметр <РежимИерархии> равен 1, то необходимо предварительно задать Владельца-элемент в ведущем справочнике через ИспользоватьВладельца() и при необходимости Родителя-группу в подчиненном справочнике через ИспользоватьРодителя(). Выборка будет сделана только по одному заданному уровню подчиненного справочника в отличие от использования обычного метода .ВыбратьЭлементы(1) для подчиненного многоуровневого справочника.

Для выборки по подчиненному многоуровневому справочнику с учетом владельца, но без учета родителя рекомендуется использовать <РежимИерархии> = 0 и отбирать по Владельцу уже при переборе элементов.

. ВыбратьЭлементыПоРеквизиту() опирается на индекс VI соответствующего реквизита, индекс VIP не используется.
Порядок следования элементов в выборке различается в зависимости установленного отбора по реквизиту (Свойство реквизита - Дополнительные - Отбор по реквизиту).
  • Если отбор не установлен - то элементы в выборке будут следовать в порядке следования записей в DBF-таблице.
  • Если отбор установлен - то элементы в выборке будут следовать в порядке ВРег(СокрЛП(Наименование)), а при одинаковых значениях наименования - в порядке следования записей в DBF-таблице.
Во всех случаях порядок следования не зависит от внутренних дескрипторов (в случае УРБД дескрипторы в таблице могут идти не по порядку), кодов, элементов, групп, владельцев, родителей, параметров <РежимИерархии> и <РежимГрупп>.

Кооперация с другими методами:
  • Функции .ПорядокКодов(), .ПорядокНаименований(), .ПорядокРеквизита(<ИмяРеквизита>) - при выборке игнорируются (ЖКК ошибается, что нет).
  • Функция .ВключатьПодчиненные() - при выборке игнорируется (ЖКК ошибается, что нет). Результат, возвращаемый функцией . ВключатьПодчиненные() от параметра <РежимИерархии> не зависит.
  • Функция .ОбратныйПорядок(<Режим>) - работает.
  • Функция .ИспользоватьДату(<Дата>,<УстСразу>) работает так:
    При вызове до выполнения .ВыбратьЭлементыПоРеквизиту() значение параметра <Дата> будет использовано для получения значений периодических реквизитов во всех последующих открытых выборках - независимо от значения параметра <УстСразу>.
    При вызове после выполнения .ВыбратьЭлементыПоРеквизиту() с параметром <УстСразу> = 1 - изменит значение даты, на которую получаются значения периодических реквизитов.
    При вызове после выполнения .ВыбратьЭлементыПоРеквизиту() с параметром <УстСразу> = 0 - не изменит значение даты, на которую получаются значения периодических реквизитов. (см.также багофичу 5)

Выборка не зависит от режима использования реквизита (параметр <ИмяРеквизита>): "Для обоих / Для элемента / Для группы", установленного в "Свойство реквизита - Дополнительные - Использовать".

Выборка не зависит от текущего объекта .(.ТекущийЭлемент(), .Владелец, .Родитель).

Помеченные на удаление группы/элементы попадают в выборку.

После успешного выполнения выборки (функция вернула 1) объект уже спозиционирован на первом объекте из выборки. Если нужен только первый объект, то выполнять .ПолучитьЭлемент() не требуется:
//заменим все Значение на ДругоеЗначение
Пока  Спр.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,Значение)=1 Цикл
 Спр.УстановитьАтрибут(ИмяРеквизита,ДругоеЗначение); 
 Спр.Записать();
КонецЦикла;

При многократных вызовах .ВыбратьЭлементыПоРеквизиту() используются однократно установленные ранее параметры выборки (.ОбратныйПорядок(), .ИспользоватьРодителя(), .ИспользоватьВладельца()). При этом выборки не объединяются (фильтры не накладываются), каждый новый вызов функции закрывает предыдущую выборку и открывает новую:
Спр.ОбратныйПорядок(1);
Спр.ВыбратьЭлементыПоРеквизиту("МойРеквизит","1234");//выборка идёт в обратном порядки
Спр.ВыбратьЭлементыПоРеквизиту("ДругойМойРеквизит","1234");//выборка также идёт в обратном порядка, фильтра по реквизиту "Мой реквизит" нет

Перебор элементов осуществляется с помощью функции .ПолучитьЭлемент(<Режим>), при этом параметр <Режим> игнорируется.

Выборка сбивается при применению к объекту, используемому для выборки, непосредственного удаления .Удалить(1). Непосредственное удаление объекта не обнулит текущую выборку, но приведёт к её некорректной работе - выборка продолжится, но некоторые элементы при переборе будут пропущены.

Выборка обнуляется после применения к объекту, используемому для выборки, следующих методов:
  • .Выбрать() - если выбор был произведен;
  • .НайтиЭлемент(), .НайтиПоКоду(), .НайтиПоНаименованию(), НайтиПоРеквизиту()
  • .ВыбратьЭлементы(), .ВыбратьЭлементыПоРеквизиту() - откроет новую выборку, обнулив текущую;
  • .Новый(), .НоваяГруппа();
  • .Записать();
Поэтому объект, используемый для выборки, не стоит использовать для записи изменений при переборе элементов - выборка будет обнулена сразу после применения .Записать():
Спр.ВыбратьЭлементыПоРекизиту(ИмяРеквизита,Значение);
Пока Спр.ПолучитьЭлемент()=1 Цикл
 Спр.Удалить(0);//пометка на удаление не обнулит выборку - выборка продолжится корректно
 Спр.Записать();//запись элемента обнулит выборку - выборка закончится
 Спр.Удалить(1);//непосредственное удаление не обнулит выборку, но приведёт к её некорректной работе - выборка продолжится, но некоторые элементы будут пропущены
КонецЦикла;

В то же время запись элементов в выборке (кроме непосредственного удаления), даже с изменением значения реквизита по которому выполняется отбор, не приведет к изменению уже сделанной выборки, если она сделана с помощью другого объекта того же типа:
Спр.ВыбратьЭлементыПоРекизиту(ИмяРеквизита,Значение);
Пока Спр.ПолучитьЭлемент()=1 Цикл
 СпрДляЗаписи.НайтиЭлемент(Спр.ТекущийЭлемент());
 СпрДляЗаписи.УстановитьАтрибут(ИмяРеквизита,ДругоеЗначение);
 СпрДляЗаписи.Записать();//выборка продолжится корректно
 СпрДляЗаписи.Удалить(0);//выборка продолжится корректно
 СпрДляЗаписи.Удалить(1);// непосредственное удаление и в этом случае приведёт к некорректной работе выборки - она продолжится, но некоторые элементы будут пропущены
КонецЦикла;

Проблемы в DBF-таблицах (например, некорректные ссылки на родителя/владельца) могут приводить к зависанию выполнения .ВыбратьЭлементыПоРеквизиту(). Лечится с помощью "Тестирование и исправление ИБ".

Всё проверялось на 27 релизе для DBF-версии.

Багофича 1, которую нужно учитывать:
При изменении режима использования реквизита "Свойство реквизита - Дополнительные - Использовать":
  • "Для обоих" -> "Для элемента"
  • "Для обоих" -> "Для группы"
  • "Для элемента" -> "Для группы"
  • "Для группы" - > "Для элемента"
значения реквизита не очищаются для групп/элементов. Эти значения перестают быть видны в списке справочника и более недоступны для изменения, даже средствами языка: для них будут проигнорированы  и Записать() в модуле формы справочника и Спр.Записать() для объектов, созданных функцией СоздатьОбъект(). При этом значения будут храниться в базе и попадать в индекс. То есть в выборку .ВыбратьЭлементыПоРеквизиту() могут попасть неожиданные элементы/группы.
Решение проблемы: перед подобным изменением режима использования следует обработкой очистить ненужные значения в справочнике.

Багофича 2, которую нужно учитывать:
Функция Спр=СоздатьОбъект("Справочник.МойСправочник") молча отработает ситуацию, когда на диалоговой форме есть реквизит диалога с таким же названием "Спр" и не являющийся справочником. В этом случае объект справочника создан не будет, но и предупреждение об ошибке не будет выдано. Поэтому последующее использование Спр.ВыбратьЭлементыПоРеквизиту() приведёт к ошибке "Поле агрегатного объекта не обнаружено". Решение проблемы: ошибка устраняется при отладке программного кода - использовать разные наименования для объекта и для реквизита диалога.

Багофича 3, которую нужно учитывать:
Признак сортировки "Свойство реквизита - Дополнительные - Сортировка'' разрешено устанавливать только для реквизитов типа "Число", "Строка", "Дата", "Справочник", "Документ", "Счет", "Календарь", "ВидРасчета".
Для реквизитов типа "Неопределенный", "ПланСчетов", "ВидСубконто" этот признак не должен устанавливаться и недоступен для редактирования. Но если  выбрать тип из первой группы, установить признак, а потом выбрать тип из второй группы, то признак сортировки хоть и станет недоступным, но останется установленным. Соответственно .ВыбратьЭлементыПоРеквизиту() можно будет вызвать с указанием этого реквизита. Для объектов типа "ПланСчетов" и "ВидСубконто"" выборка будет работать как обычно, хотя такая возможность не подразумевалась. Для объектов типа "Неопределенный", несмотря на корректно заполненные значения реквизита, результат выборки всегда будет пустой.
Решение проблемы: снять некорректно установленную галку можно аналогично: выбрать тип из первой группы, снять признак, а потом выбрать нужный  тип из второй группы.

Багофича 4, которую нужно учитывать:
Если реквизит отбора не строковый, а тип параметра <Значение> не соответствует типу реквизита, то <Значение> будет приведено к типу реквизита. При этом если окажется, что в справочнике есть элементы, удовлетворяющие отбору по этому приведенному значению, то выборка будет успешна (.ВыбратьЭлементыПоРеквизиту() вернёт 1), но после первого выполнения ПолучитьЭлемент() выборка будет завершена, то есть будет отдан только один элемент (первый в выборке):
//Если в справочнике есть реквизит отбора ИмяРеквизита Число(5.1) и два элемента, у которых ИмяРеквизита=123.0, то
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"123.0") - в результате перебора будет отдан только первый элемент (некорректная выборка);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,123.05) - аналогично (некорректная выборка);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,123.0) - в результате перебора будут отданы все элементы;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,123) - аналогично.
//Если в справочнике есть реквизит отбора ИмяРеквизита типа "Дата" и два элемента, у которых ИмяРеквизита='20.08.2024', а у остальных пусто, то:
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,'20.08.2024') - в результате перебора будут отданы все элементы у которых ИмяРеквизита='20.08.2024;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,'20.08.24') - аналогично, если год начала рабочего столетия (Сервис-Параметры-Год начала рабочего столетия) после 1924, а иначе - пустая выборка;
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"20.08.2024") - в результате перебора будет отдан только первый элемент, у которого ИмяРеквизита='20.08.2024' (некорректная выборка);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"20.08.24") - аналогично (с учетом года начала рабочего столетия) (некорректная выборка);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,"20082024") - в результате перебора будет отдан только первый элемент из тех, у которых ИмяРеквизита - пусто, так как в этом случае результат преобразования Дата(<Значение>) - пустая дата, но при этом <Значение> не равно ПолучитьПустоеЗначение("Дата") (некорректная выборка);
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита) - аналогично (некорректная выборка);
//Если в справочнике есть реквизит отбора ИмяРеквизита типа "Справочник.МойСправочник", то есть определенного вида, то
.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,ПолучитьПустоеЗначение("Справочник")) - в результате перебора будет отдан только первый элемент, у которого ИмяРеквизита - пусто (некорректная выборка).
Решение проблемы: следить, чтобы типы значений в параметре <Значение>, и заданного в конфигураторе реквизита, указанного в параметре < ИмяРеквизита >, строго соответствовали друг другу.

Багофича 5, которую нужно учитывать:
Функция .ИспользоватьДату(<Дата>,<УстСразу>) -  впервые вызванная после выполнения .ВыбратьЭлементыПоРеквизиту() с параметром <УстСразу> = 0 не изменит значение даты, на которую получаются значения периодических реквизитов, но переключит режим получения этих значений: метод .Получить(<Дата>) перестанет работать. При этом, так как дата не была установлена ранее, а текущая установка заблокирована параметром <УстСразу> = 0, то обращение к периодическим реквизитам будет возвращать пустое значение (значение на пустую дату?):
Спр.ВыбратьЭлементыПоРеквизиту(ИмяПараметра,Значение);
Спр.ИспользоватьДату(ДатаЗначений);//параметр <УстСразу> - не задан, установка даты для текущей выборки не действует
Пока Спр.ПолучитьЭлемент()=1 Цикл  
 а=Спр.ПериодическийРеквизит.Получить(ДатаЗначений);//этот метод больше не работает, будет выдана ошибка
 а=Спр.ПериодическийРеквизит;//а этот метод всегда возвращает пустое значение
КонецЦикла;
Решение проблемы: вызывать .ИспользоватьДату() до открытия выборки. При необходимости изменения даты после открытия выборки - обязательно указывать параметр <УстСразу> = 1.


...если бы я раньше знал, какая это канитель — писать книжку, то нипочем бы не взялся, и больше уж я писать никогда ничего не буду. © Гек Финн

trdm


Злоп


Харлампий Дымба

Просто все 25 лет раздражала эта функция своим странным поведением и непредсказуемостью. А теперь для себя разобрался со всеми своими вопросам.

Главных вещей по итогу 2:
  • метод не умеет в иерархию;
  • тип значения отбора должен быть строго равен типу реквизита отбора.
Поэтому всегда вызываем только так:
Спр.ВыбратьЭлементыПоРеквизиту(ИмяПараметра,Значение,0); //а уже потом при переборе при необходимости отбираем .Владелец, .Родитель, .ЭтоГруппа()
Исключение - одноуровневый подчиненный справочник. Тогда можно так:
Спр.ИспользоватьВладельца(Владелец);
Спр.ВыбратьЭлементыПоРеквизиту(ИмяПараметра,Значение,1);
Остальное - беллетристика.

Харлампий Дымба

Ёлки, это никогда не кончится(
Посмотрел код типовой ТиС и нашел ещё один косяк - использование .ВыбратьЭлементыПоРеквизиту(ИмяПараметра,Значение,1,1) для одноуровневых подчинённых справочников ведёт к проблемам.
Дополнение (там в середине исходного поста был список по поведению выборки для разных вариантов справочников):

Если параметр <РежимИерархии> равен 1 и параметр <РежимГрупп> равен 1 то:
  • Если справочник подчиненный одноуровневый: параметр <РежимГрупп> будет проигнорирован и выборка будет сделана не по группам, а по элементам. Но при переборе, после первого полученного элемента, выборка будет обнулена (подобное поведение описано в Багофиче 4):
    Спр.ИспользоватьВладельца(Владелец);                                              
    Спр.ВыбратьЭлементыПоРеквизиту(ИмяРеквизита,Значение,1,1);
    Пока Спр.ПолучитьЭлемент()=1 Цикл  
     //после первого полученного элемента выборка завершится (видимо по несоответствию ISFOLDER и <РежимГрупп>)
    КонецЦикла;
    
     
  • Если справочник одноуровневый: параметр <РежимГрупп> будет проигнорирован и выборка будет корректно сделана не по группам, а по элементам. В перебор попадут все выбранные элементы.

Ветер в поле

Так как я работаю с SQL-версией 1С, то всякие такие переборы очень негативно сказываются на производительности. С другой стороны моя конфигурация работает и в DBF, поэтому прямые запросы тоже не катят. Можно использовать КОП ПрямойЗапрос, но это не слишком удобно, в нем есть достаточно неприятные глюки (скорее это проблема в 1cpp), ну и время подготовки не маленькое - ни о каких тысяче запросов в секунду речи не идет (раз в 30 медленнее). Поэтому я сделал свой КОП ЗапросКСправочнику, который заточен, как понятно из наименования, именно для выборок из справочников. Он работает и на SQL и на DBF.
оСпрКонтр = СоздатьОбъект("ЗапросКСправочнику");
оСпрКонтр.Инит("Контрагенты");
оСпрКонтр.ВыбиратьПервые = 10;
оСпрКонтр.ИспользоватьРодителя(Группа);
оСпрКонтр.ДобавитьРеквизиты("ТекущийЭлемент,Наименование");
оСпрКонтр.ДобавитьУсловие("ВидКонтрагента", Значение, "=");
оСпрКонтр.ДобавитьСортировку("Наименование", "+");
тзКонтр = оСпрКонтр.ВыполнитьЗапрос();

тзКонтр.ВыбратьСтроки();
Пока тзКонтр.ПолучитьСтроку() = 1 Цикл
    ТекКонтрагент = тзКонтр.ТекущийЭлемент;
    
КонецЦикла;

Скорость для DBF сравнима с классическим Справочником, т.е. в районе 2000 запросов в секунду, а для SQL примерно в 20 раз быстрее - около 800 запросов в секунду. И то это если в лоб действовать. Если же использовать параметрические запросы, то будет еще в полтора-два быстрее.

Самое главное, что тут я могу дополнительные условия поставить, а не одно, ну и сделать соединение с другими справочниками и одним запросом получить все данные. Помимо упрощения кода скорость часто увеличивается в десятки раз. Причем переписать на использование КОП большую конфигурацию удалось буквально за несколько дней, а ошибок даже и не припомню.

Больше всего я горжусь методом ИспользоватьСписокЭлементов, который позволяет построить запрос по списку элементов. Вот так:
оСпр = СоздатьОбъект("ЗапросКСправочнику");
оСпр.Инит("Пациенты");
оСпр.ИспользоватьСписокЭлементов(спЭлем);
оСпр.ДобавитьРеквизиты("ТекущийЭлемент,Код");
тзЭлем = оСпр.ВыполнитьЗапрос();

тзЭлем.ВыбратьСтроки();
Пока тзЭлем.ПолучитьСтроку() = 1 Цикл
    ТекКод = тзЭлем.Код;
КонецЦикла;

Скорость вырастает в 40 раз для SQL и 3 раза для DBF. Для SQL в районе 20 мс на 1000 элементов, а для DBF 8 мс.

Злоп


Злоп


trad

VI - индекс по реквизиту с флагом "Сортировка"
VIP - такой же индекс как VI, но дополнен полем родителя(P-parent) у подчиненного справочника (parentext) или у cправочника с иерархией (parentid+isfolder)

Харлампий Дымба

Честно говоря, вот это:

  • Функция .ОбратныйПорядок(<Режим>) - работает.

было слишком оптимистично. В некоторых режимах есть определенные особенности.

Злоп

Короче: чем проще - тем лучше.
Навскидку сколько помню - в основном использовал для подчиненных справочников без иерархии, либо для обычных справочников только по элементам.