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