Ссылка в запросе 1С встречается в трёх ролях: как поле Ссылка, по которому отбирают конкретный документ или элемент справочника; как операнд оператора ССЫЛКА, который проверяет тип значения в поле составного типа; и как отправная точка для получения реквизитов «через точку». С первым всё просто, а на втором и третьем ошибаются чаще всего: запрос по регистру с Регистратор.Номер выглядит безобидно, а в SQL превращается в длинную цепочку соединений. Ниже — синтаксис для платформы 8.3, готовый пример для регистра накопления и разбор того, во что обходится разыменование. Проверка на пустую ссылку и её отличие от NULL разобраны отдельно, в статье «Пустая ссылка в запросе».
У каждой таблицы ссылочного объекта (справочника, документа, плана видов характеристик, плана счетов) есть стандартный реквизит Ссылка. Это уникальный ключ записи, и по нему в таблице всегда есть индекс, поэтому отбор по ссылке — самый быстрый способ найти одну запись. Ссылку на документ в запрос передают параметром:
&НаСервереБезКонтекста
Функция ДанныеРеализации(Знач Ссылка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Номер КАК Номер,
| Реализация.Дата КАК Дата,
| Реализация.Контрагент КАК Контрагент,
| Реализация.Контрагент.ИНН КАК ИННКонтрагента
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Результат = Новый Структура("Номер, Дата, Контрагент, ИННКонтрагента");
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ЗаполнитьЗначенияСвойств(Результат, Выборка);
КонецЕсли;
Возврат Результат;
КонецФункции
Функция — для модуля формы: объекта Запрос на клиенте нет, а данные формы не нужны, отсюда &НаСервереБезКонтекста. Чтобы отобрать несколько документов, условие меняют на Реализация.Ссылка В (&Ссылки) и передают в параметр массив ссылок. У табличной части тоже есть поле Ссылка: в таблице Документ.РеализацияТоваровУслуг.Товары это ссылка на документ-владелец, так что Товары.Ссылка.Дата вернёт дату из шапки. Основы синтаксиса — в статье о языке запросов, выборка документов за период — в материале про запрос к документам.
В конфигурациях на БСП то же делают функции серверного модуля ОбщегоНазначения: ЗначениеРеквизитаОбъекта(Ссылка, "Контрагент") и ЗначенияРеквизитовОбъекта(Ссылка, "Номер, Дата, Контрагент"), вторая возвращает структуру. Они есть в БСП 2.x и 3.x, но необязательные параметры в редакциях отличаются — сверяйтесь с описанием в своей конфигурации.
Оператор ССЫЛКА отвечает на вопрос «ссылка какого вида лежит в этом поле». Синтаксис: <Выражение> ССЫЛКА <ИмяТаблицы>. Имя таблицы пишется так же, как в секции ИЗ, в единственном числе и без кавычек: Документ.РеализацияТоваровУслуг, Справочник.Контрагенты. Результат логический, поэтому оператор ставят в ГДЕ и в ветки ВЫБОР КОГДА.
Нужен он там, где у поля составной тип:
| Поле | Где встречается | Что в нём лежит |
|---|---|---|
Регистратор | регистры накопления, бухгалтерии, расчёта; регистры сведений, подчинённые регистратору | ссылка на любой документ, который делает движения по регистру |
СубконтоДт1, СубконтоКт1 и следующие (в регистре без корреспонденции — Субконто1 и следующие) | таблица движений регистра бухгалтерии | значение любого типа, разрешённого для вида субконто: чаще ссылка на справочник, документ или перечисление, реже примитивное значение |
Ссылка журнала документов | журналы документов | ссылка на один из документов, входящих в журнал |
| Реквизиты вроде «Основание», «Объект», «Владелец» | справочники и документы с реквизитом составного типа | набор типов, который задал разработчик |
Типичный случай — оставить в выборке движения только одного вида документов:
ВЫБРАТЬ
Продажи.Регистратор КАК Регистратор,
Продажи.Номенклатура КАК Номенклатура,
Продажи.Количество КАК Количество
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
И Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Отрицание записывается через НЕ: НЕ Продажи.Регистратор ССЫЛКА Документ.КорректировкаРеализации. Несколько видов перечисляют через ИЛИ, и если рядом есть условия через И, группу ИЛИ обязательно берут в скобки.
Три свойства оператора, которые стоит держать в голове:
ССЫЛКА сравнивает только её — без соединения с таблицей документа.ССЫЛКА Документ.РеализацияТоваровУслуг удовлетворяет. Заполненность проверяется отдельно — сравнением с пустой ссылкой.ГДЕ, а в ВЫБОР уйдёт в ветку ИНАЧЕ.Тот же результат даёт сравнение типов: ТИПЗНАЧЕНИЯ(Продажи.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг). Запись длиннее, зато тип можно вынести в параметр: условие ТИПЗНАЧЕНИЯ(Продажи.Регистратор) В (&ТипыДокументов) с массивом значений вида Тип("ДокументСсылка.РеализацияТоваровУслуг") удобно, когда список видов собирается в коде по настройкам.
Запись Реализация.Контрагент.ИНН — сокращение. СУБД не умеет переходить по ссылкам, поэтому при трансляции запроса платформа добавляет неявное левое соединение: таблицу документов соединяет с таблицей контрагентов по ссылке и берёт ИНН оттуда. Каждая точка после ссылочного поля — ещё одно соединение: Реализация.Контрагент.ГоловнойКонтрагент.ИНН даст два. Для поля одного типа это приемлемо: соединение идёт по индексу и стоит как такое же, написанное вручную.
С составным типом картина другая. Платформа заранее не знает, документ какого вида лежит в Регистратор, и соединяет таблицу регистра со всеми таблицами, ссылки на которые могут быть в этом поле. Если регистраторов у регистра двадцать, соединений будет двадцать — и это ради одного Продажи.Регистратор.Дата. У реквизитов с типом «любая ссылка» или «любой документ» счёт идёт на все таблицы такого вида в конфигурации. Если в базе настроено ограничение доступа на уровне записей, к присоединённым таблицам могут добавиться ещё и условия ограничений. Оптимизатору СУБД с таким текстом тяжело, и запрос, мгновенный на тестовой базе, на рабочей может идти минутами. Об этом отдельно предупреждает стандарт 1С «Разыменование ссылочных полей составного типа в языке запросов».
Лечится это функцией ВЫРАЗИТЬ. Было:
ВЫБРАТЬ
Продажи.Регистратор.Контрагент КАК Контрагент,
Продажи.Сумма КАК Сумма
ИЗ
РегистрНакопления.Продажи КАК Продажи
Стало:
ВЫБРАТЬ
ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент,
Продажи.Сумма КАК Сумма
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ВЫРАЗИТЬ сообщает платформе: «здесь реализация». Соединение строится с одной таблицей, а для строк, где в поле документ другого вида, результат будет NULL. Поэтому ВЫРАЗИТЬ почти всегда идёт в паре с ССЫЛКА: оператор отсекает чужие строки, функция даёт дешёвый доступ к реквизитам. Для нескольких видов документов — ВЫБОР с отдельным ВЫРАЗИТЬ на каждый вид или явные соединения, как в полном примере ниже.
Практические правила:
Номенклатура.Родитель.Родитель.Наименование — уже повод для временной таблицы.ВЫРАЗИТЬ или явное соединение.Период, и Регистратор.Дата вместо него — лишние соединения, а иногда и другое значение.Если ссылка пустая, поле через точку от неё вернёт NULL: пары в присоединённой таблице нет. То же будет с полем составного типа, если нужного реквизита у документа этого вида нет.
Функция собирает продажи за период по реализациям и возвратам от клиентов и добавляет номер, дату и контрагента документа. В ней всё сказанное выше: ССЫЛКА для отбора, группировка до соединений и явные соединения вместо точки от регистратора.
// Серверный общий модуль или модуль объекта отчета/обработки.
// Имена объектов метаданных условные: замените их на имена из своей конфигурации.
// Возвращает продажи за период в разрезе документов-регистраторов
// с номером, датой и контрагентом документа. Учитываются реализации
// и возвраты от клиентов, движения остальных документов отбрасываются.
//
// Параметры:
// ДатаНачала - Дата - начало периода.
// ДатаОкончания - Дата - конец периода.
//
// Возвращаемое значение:
// ТаблицаЗначений - колонки Регистратор, ВидДокумента, Номер, Дата,
// Контрагент, Количество, Сумма.
//
Функция ПродажиПоДокументам(Знач ДатаНачала, Знач ДатаОкончания) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Продажи.Регистратор КАК Регистратор,
| СУММА(Продажи.Количество) КАК Количество,
| СУММА(Продажи.Сумма) КАК Сумма
|ПОМЕСТИТЬ ВТ_Регистраторы
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|ГДЕ
| Продажи.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
| И (Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
| ИЛИ Продажи.Регистратор ССЫЛКА Документ.ВозвратТоваровОтКлиента)
|
|СГРУППИРОВАТЬ ПО
| Продажи.Регистратор
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Регистраторы.Регистратор КАК Регистратор,
| ВЫБОР
| КОГДА Регистраторы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
| ТОГДА ""Реализация""
| ИНАЧЕ ""Возврат""
| КОНЕЦ КАК ВидДокумента,
| ЕСТЬNULL(Реализация.Номер, Возврат.Номер) КАК Номер,
| ЕСТЬNULL(Реализация.Дата, Возврат.Дата) КАК Дата,
| ЕСТЬNULL(Реализация.Контрагент, Возврат.Контрагент) КАК Контрагент,
| Регистраторы.Количество КАК Количество,
| Регистраторы.Сумма КАК Сумма
|ИЗ
| ВТ_Регистраторы КАК Регистраторы
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
| ПО Регистраторы.Регистратор = Реализация.Ссылка
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтКлиента КАК Возврат
| ПО Регистраторы.Регистратор = Возврат.Ссылка
|
|УПОРЯДОЧИТЬ ПО
| Дата";
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
РАЗРЕШЕННЫЕ запрос завершился бы ошибкой «Недостаточно прав».Регистратор.Номер, который соединил бы регистр со всеми таблицами регистраторов, — ровно две таблицы. Для реализации пара найдётся только в первой, поля второй будут NULL, поэтому ЕСТЬNULL берёт значение из той таблицы, где пара нашлась. ССЫЛКА в условии соединения не нужна: равенство ссылок уже включает совпадение типа.ИНАЧЕ безопасна: первый запрос оставил только два вида.КонецДня движения последнего дня выпали бы из выборки.Вариант с ВЫРАЗИТЬ тоже сработал бы, но три поля на два вида документов — это шесть выражений внутри ВЫБОР, читать такое тяжелее.
Регистратор ССЫЛКА Документы.РеализацияТоваровУслуг или ССЫЛКА "Документ.РеализацияТоваровУслуг" — запрос не выполнится. Платформа сообщит об ошибке в тексте запроса и укажет строку и позицию в фигурных скобках, например {(9, 33)}. Справа всегда имя таблицы, как в секции ИЗ.Реализация.Контрагент.ИН вместо ИНН даст ошибку «Поле не найдено» с именем поля. Всплывёт она только при выполнении; заранее её покажет конструктор запроса.ВЫРАЗИТЬ или явное соединение.Выборка.Контрагент.ИНН в коде — это чтение из базы на каждой строке с новым контрагентом, причём платформа считывает объект целиком, а не одно поле. Нужный реквизит получайте в самом запросе.&НаКлиенте обращение Объект.Контрагент.ИНН завершится ошибкой «Поле объекта не обнаружено (ИНН)»: на клиенте ссылка не даёт доступа к реквизитам объекта. Значение нужно получить на сервере.По результату — ничем: Поле ССЫЛКА Документ.X и ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.X) отбирают одни и те же строки. ССЫЛКА короче и читается проще. ТИПЗНАЧЕНИЯ нужен, когда тип приходит параметром или его надо вывести в результат запроса как отдельную колонку.
Функцией ТипЗнч(): ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг"). Чтобы проверить, что значение — ссылка на любой документ, используют описание типов от менеджера: Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)). Для справочников — аналогично через Справочники.ТипВсеСсылки().
Да: набор данных СКД и запрос динамического списка пишутся на том же языке запросов. В отчётах на СКД по регистрам ВЫРАЗИТЬ особенно полезен — поле через точку от регистратора замедляет каждое формирование.
Период записывает код проведения. Обычно это дата документа, но код может записать и другую дату, например дату отгрузки из табличной части. Для отчётов по движениям правильнее брать Период: он уже есть в таблице регистра и не требует соединений.
Если отчёт по регистру тормозит из-за разыменований или нужен новый отчёт на СКД с данными документов-регистраторов, задачу можно передать программисту. На 1c-only.ru можно заказать разработку отчётов и обработок 1С или разместить задачу: оценка бесплатная, ответ в течение рабочего дня. Если медленно работает не один отчёт, а вся база, посмотрите услугу «1С тормозит».
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.