Фильтр по виду документа в языке запросов: регистратор, составные реквизиты, журналы
Содержание
Чтобы в запросе 1С отобрать записи по типу документа, есть два рабочих условия: Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг или ТИПЗНАЧЕНИЯ(Регистратор) = ТИП(Документ.РеализацияТоваровУслуг). Оба отбирают только строки, где в поле лежит ссылка на документ нужного вида. Первое короче и читается легче, второе можно параметризовать и проверять сразу список типов.
Типичная ситуация: бухгалтер просит выгрузить из регистра бухгалтерии только движения реализаций, без корректировок и ручных операций. Поле Регистратор там составного типа, и обычное сравнение с одной ссылкой не подходит. Ниже — как такие поля устроены, какой оператор когда брать и где запросы чаще всего ломаются. Всё сказанное относится к платформе 8.3 и не зависит от конфигурации: Бухгалтерия, ЗУП, УТ или своя разработка.
Как платформа хранит ссылку составного типа
Отбор по типу документа нужен там, где поле может ссылаться на документы разных видов. Таких полей три группы:
- 📌
Регистраторв таблицах регистров накопления, бухгалтерии и сведений, подчинённых регистратору; - 📌 реквизиты составного типа:
ДокументОснование,Сделка,Субконто1–Субконто3в регистре бухгалтерии; - 📌 поле
Ссылкажурнала документов, куда входит несколько видов документов.
В базе данных такое поле занимает не одну колонку, а несколько. Для поля, которое может содержать только ссылки, хранится номер таблицы (какой это вид документа) и сам уникальный идентификатор ссылки. Если в составной тип входят ещё примитивные типы (строка, число, дата), добавляется колонка с признаком типа и колонки под каждый примитив.
Из этого следуют два практических вывода:
- Условие по типу документа превращается в SQL в простое сравнение номера таблицы. Оно дешёвое, соединений с таблицами документов не требует.
- Обращение «через точку» к такому полю (
Регистратор.Дата,Регистратор.Контрагент) заставляет платформу соединить запрос со всеми таблицами документов, которые входят в составной тип. У регистра с 30 регистраторами это 30 левых соединений.
Если регистр подчинён только одному виду документа, поле Регистратор у него не составное. Отбор по типу там бессмыслен: все записи и так от одного документа.
Отбор через ТИПЗНАЧЕНИЯ и ТИП
Функция ТИПЗНАЧЕНИЯ() возвращает тип значения поля, функция ТИП() — тип по имени таблицы. Их сравнение и даёт отбор по виду документа.
ВЫБРАТЬ
Хозрасчетный.Регистратор КАК Документ,
Хозрасчетный.СчетДт,
Хозрасчетный.СчетКт,
Хозрасчетный.Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)
И Хозрасчетный.Период МЕЖДУ &НачалоПериода И &КонецПериода
Внутри ТИП() пишут имя таблицы так же, как в секции ИЗ: Документ.РеализацияТоваровУслуг. Ни кавычек, ни слова ДокументСсылка там не нужно.
Самая частая ошибка — перенести в текст запроса синтаксис встроенного языка. В коде модуля пишут Тип("ДокументСсылка.РеализацияТоваровУслуг"), а в запросе — ТИП(Документ.РеализацияТоваровУслуг). Перепутаете — получите синтаксическую ошибку при выполнении.
Главное преимущество ТИПЗНАЧЕНИЯ — тип можно передать параметром:
ГДЕ
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) = &ТипДокумента
Запрос.УстановитьПараметр("ТипДокумента", Тип("ДокументСсылка.РеализацияТоваровУслуг"));
Не переносите отбор из запроса в цикл по выборке с проверкой ТипЗнч(Выборка.Документ) = Тип("ДокументСсылка.РеализацияТоваровУслуг"). Результат тот же, но с сервера СУБД придут все строки регистра, а отбросятся они уже на сервере 1С. На периоде в год это разница между секундами и минутами.
Так один текст запроса работает для любого вида документа, который выбрал пользователь в форме отчёта или обработки.
Тип в колонке результата
ТИПЗНАЧЕНИЯ() годится не только для условия. Её можно вывести в выборку и сгруппировать по ней, чтобы посчитать обороты по видам документов:
ВЫБРАТЬ
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) КАК ВидДокумента,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Хозрасчетный.Регистратор) КАК Документов,
СУММА(Хозрасчетный.Сумма) КАК Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор)
В колонке ВидДокумента окажется значение типа Тип. Человекочитаемое имя в коде получают так: Метаданные.НайтиПоТипу(Выборка.ВидДокумента).Синоним.
Оператор ССЫЛКА: проверка, что значение из таблицы документа
Оператор ССЫЛКА проверяет, ссылается ли значение на указанную таблицу. По сути это вопрос «есть ли эта ссылка среди ссылок такого вида документа», только без подзапроса.
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Отрицание и комбинация нескольких видов пишутся через обычные НЕ и ИЛИ:
ГДЕ
(Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ИЛИ Хозрасчетный.Регистратор ССЫЛКА Документ.ВозвратТоваровОтПокупателя)
И НЕ Хозрасчетный.Регистратор ССЫЛКА Документ.ОперацияБух
ССЫЛКА удобен ещё и в ВЫБОР: например, чтобы в одной колонке показать дату документа без соединения со всеми таблицами регистраторов, а в другой — признак «отгрузка или возврат».
ВЫБОР
КОГДА Т.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Отгрузка"
КОГДА Т.Регистратор ССЫЛКА Документ.ВозвратТоваровОтПокупателя
ТОГДА "Возврат"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидОперации
Ограничение у оператора одно, но существенное: справа стоит имя таблицы, а не выражение. Параметр туда подставить нельзя. Нужен выбор вида документа «на лету» — берите ТИПЗНАЧЕНИЯ.
Подзапрос и временная таблица как альтернатива
Иногда отбор нужен не по виду документа целиком, а по документам, отобранным по своим условиям: проведённые реализации одного контрагента, документы конкретного склада. Тогда к оператору типа добавляют В (ВЫБРАТЬ …) или соединение с временной таблицей:
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Документы
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Контрагент = &Контрагент
И Реализация.Проведен
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
ВЫБРАТЬ
Хозрасчетный.Регистратор,
Хозрасчетный.Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Регистратор В
(ВЫБРАТЬ ВТ_Документы.Ссылка ИЗ ВТ_Документы КАК ВТ_Документы)
Условие ССЫЛКА здесь уже не нужно: во временной таблице только ссылки на реализации.
Несколько видов документов: список типов, журнал, объединение
Когда видов документов больше двух-трёх, цепочка ИЛИ с ССЫЛКА становится громоздкой. Есть три способа короче.
Список типов параметром. Передайте массив типов и сравните через В:
ГДЕ
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) В (&ТипыДокументов)
ТипыДокументов = Новый Массив;
ТипыДокументов.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
ТипыДокументов.Добавить(Тип("ДокументСсылка.ВозвратТоваровОтПокупателя"));
Запрос.УстановитьПараметр("ТипыДокументов", ТипыДокументов);
Журнал документов
. У журнала есть стандартный реквизит Тип, где хранится вид документа. Отбор по нему пишут так:
ВЫБРАТЬ
Журнал.Ссылка,
Журнал.Дата,
Журнал.Номер
ИЗ
ЖурналДокументов.ИмяЖурнала КАК Журнал
ГДЕ
Журнал.Тип = ТИП(Документ.РеализацияТоваровУслуг)
Подставьте имя журнала из своей конфигурации: оно видно в дереве метаданных конфигуратора, ветка «Журналы документов».
Объединение таблиц документов. Если журнала нет, а нужны поля самих документов (контрагент, сумма), надёжнее собрать выборку через ОБЪЕДИНИТЬ ВСЕ по каждой таблице. Каждый кусок читает свою таблицу напрямую, без составных полей. Такой текст длиннее, зато каждое поле берётся без лишних соединений.
| Способ | Когда подходит | Минусы |
|---|---|---|
ССЫЛКА | 1–3 вида, известны заранее | Не параметризуется |
ТИПЗНАЧЕНИЯ() = &Тип | Вид выбирает пользователь | Чуть длиннее запись |
ТИПЗНАЧЕНИЯ() В (&Типы) | Список видов меняется | Нужен код заполнения массива |
Журнал + Тип | Документы уже в одном журнале | Журнал есть не всегда, в нём только графы журнала |
ОБЪЕДИНИТЬ ВСЕ | Нужны реквизиты самих документов | Длинный текст запроса |
Отбор по иерархии и группировка по видам
Документы не бывают иерархическими, но фильтр по типу часто сочетают с отбором по иерархическому справочнику. Пример: все реализации покупателям из одной группы контрагентов.
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
И Хозрасчетный.СубконтоДт1 В ИЕРАРХИИ (&ГруппаКонтрагентов)
В ИЕРАРХИИ отбирает и саму группу, и всех её потомков на любой глубине. Работает только для иерархических справочников и планов видов характеристик.
Здесь есть ловушка. СубконтоДт1 — тоже поле составного типа: на одном счёте там контрагент, на другом номенклатура. Платформа сравнит только значения нужного типа, но для ясности и скорости полезно добавить ограничение по счёту или по типу субконто.
Для отчёта с разбивкой «вид документа → документ» используйте ИТОГИ:
ИТОГИ
СУММА(Сумма)
ПО
ВидДокумента,
Документ
Чтобы это сработало, выведите в выборку обе колонки: ТИПЗНАЧЕНИЯ(Регистратор) КАК ВидДокумента и Регистратор КАК Документ. В системе компоновки данных то же самое делают группировкой по полю ВидДокумента с вложенной группировкой по документу.
Какой способ быстрее и что на самом деле тормозит
Между ССЫЛКА и ТИПЗНАЧЕНИЯ() = ТИП() разницы в скорости на практике нет. Оба превращаются в сравнение колонки с номером таблицы. Выбирайте по удобству.
Запрос тормозит из-за другого:
- 🐢 Разыменование составного поля.
Регистратор.Контрагентпорождает соединения со всеми таблицами регистраторов. Решение —ВЫРАЗИТЬ:
ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
Теперь соединение одно — с таблицей реализаций. Для строк других видов поле вернёт NULL.
- 🐢 Условие на виртуальную таблицу в
ГДЕ. ДляОбороты(…)с периодичностью «Регистратор» или «Запись» условие по виду документа ставят в параметрУсловиевиртуальной таблицы. Тогда отбор выполняется до расчёта оборотов, а не после. Пример для регистра накопления с оборотами по регистраторам:
ВЫБРАТЬ
Обороты.Регистратор,
Обороты.Номенклатура,
Обороты.КоличествоОборот
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(
&НачалоПериода,
&КонецПериода,
Регистратор,
Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг) КАК Обороты
Третий параметр задаёт периодичность, четвёртый — условие. Имя регистра подставьте своё: набор регистров в каждой конфигурации отличается. Если условие перенести в ГДЕ, результат будет тем же, но платформа сначала посчитает обороты по всем документам, а потом отбросит лишнее.
- 🐢 Отбор по типу без периода. В регистре на миллионы записей условие только по виду документа читает почти всю таблицу. Добавляйте период или другое условие по индексированному полю.
- 🐢
ИЛИна десяток видов. Оптимизатор СУБД иногда строит по нему плохой план. Замените наВ (&Типы)или вынесите выборку во временную таблицу.
Проверить, сколько таблиц реально участвует в запросе, удобно в технологическом журнале или в профилировщике СУБД. Признак проблемы — десятки LEFT JOIN к таблицам _Document… в тексте SQL.
У Остатки(…) и ОстаткиИОбороты(…) без периодичности «Регистратор» поля Регистратор нет совсем. Там отбирать по виду документа не по чему: сначала нужно перейти к реальной таблице движений или к оборотам с нужной периодичностью.
Пустые ссылки, NULL и НЕОПРЕДЕЛЕНО
В составных полях живут три разных «пустых» значения, и условие по типу на каждое реагирует по-своему.
| Значение | Откуда берётся | Как отобрать |
|---|---|---|
НЕОПРЕДЕЛЕНО | Составной реквизит ни разу не заполняли | Поле = НЕОПРЕДЕЛЕНО |
| Пустая ссылка вида | Выбрали тип, но не выбрали документ | Поле = ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка) |
NULL | Левое соединение не нашло строку, ВЫРАЗИТЬ к чужому виду | Поле ЕСТЬ NULL |
Неочевидная деталь: пустая ссылка вида проходит проверку по типу. ДокументОснование ССЫЛКА Документ.РеализацияТоваровУслуг вернёт истину и для пустой реализации. Если нужны только заполненные значения, добавьте второе условие:
ГДЕ
Док.ДокументОснование ССЫЛКА Документ.РеализацияТоваровУслуг
И Док.ДокументОснование <> ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка)
С NULL другая беда. Условие вида НЕ Поле ССЫЛКА … после левого соединения ведёт себя не так, как ожидают: строки с NULL могут попасть в выборку или выпасть из неё не по вашему замыслу. Не полагайтесь на отрицание. Пропишите явно, что делать с NULL: И НЕ Поле ЕСТЬ NULL или, наоборот, ИЛИ Поле ЕСТЬ NULL.
Поле Регистратор в записях регистра пустым не бывает: запись всегда принадлежит документу. Пустые значения появляются в реквизитах документов и в субконто.
Поле ССЫЛКА Документ.Имя, несколько видов — через ИЛИ. Нужны реквизиты документа — ВЫРАЗИТЬ, а не обращение через точку.Частые вопросы
Можно ли отобрать по типу документа в отчёте на СКД без правки запроса?
ТИПЗНАЧЕНИЯ(Регистратор) КАК ВидДокумента или параметр типа, тогда отбор появится в настройках.Как в запросе получить имя вида документа строкой?
ТИПЗНАЧЕНИЯ(Регистратор), а имя получите в коде: Метаданные.НайтиПоТипу(Тип).Имя или .Синоним. Для отчёта проще построить колонку через ВЫБОР с ССЫЛКА.Почему запрос с ТИП("ДокументСсылка…") выдаёт ошибку?
ТИП(Документ.Имя) без кавычек, а Тип("ДокументСсылка.Имя") используют только при заполнении параметра.Работает ли ССЫЛКА для справочников и других объектов?
Субконто1 ССЫЛКА Справочник.Контрагенты, Поле ССЫЛКА ПланВидовХарактеристик.Имя.Как исключить ручные операции из выборки по регистру бухгалтерии?
НЕ Регистратор ССЫЛКА Документ.ОперацияБух. Имя документа ручной операции проверьте в метаданных своей конфигурации.