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