При разработке отчетов и обработок в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда запрос, связывающий документы и регистры сведений, возвращает пустую таблицу. При этом визуально в пользовательском интерфейсе все данные заполнены, элементы справочников названы одинаково, а типы полей совпадают. Разберем подробно, почему возникает такая проблема, как ее диагностировать и как правильно строить запросы к виртуальным таблицам с точки зрения архитектуры и производительности 1С.
ВНУТРЕННЕЕ СОЕДИНЕНИЕ не находит данныеРассмотрим исходную ситуацию. Разработчик пытается получить тарифы сотрудников на основе данных из документов путевых листов с помощью следующего запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
титПутевойЛист.ЧасыВРаботе КАК ЧасыВРаботе,
титПутевойЛист.Водитель КАК Водитель,
ТарифыЗПСрезПоследних.Сотрудник КАК Сотрудник,
ТарифыЗПСрезПоследних.Тариф КАК Тариф,
ТарифыЗПСрезПоследних.Сумма КАК Сумма
ИЗ
Документ.ПутевойЛист КАК титПутевойЛист
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ТарифыЗП.СрезПоследних КАК ТарифыЗПСрезПоследних
ПО титПутевойЛист.Водитель = ТарифыЗПСрезПоследних.Сотрудник
При выполнении данного запроса результат оказывается пустым, хотя данные о тарифах в регистре занесены, а документы проведены. Выясним основные причины, по которым это происходит.
Самая распространенная причина, по которой ВНУТРЕННЕЕ СОЕДИНЕНИЕ возвращает пустой результат — это несовпадение ссылочных значений в поле связи. В платформе 1С сравнение титПутевойЛист.Водитель = ТарифыЗПСрезПоследних.Сотрудник происходит не по наименованию или коду, а по уникальному идентификатору записи базы данных (GUID).
Если в системе созданы два элемента справочника с одинаковым наименованием (например, два элемента «Иванов И.И.» с разными кодами), то для платформы это абсолютно разные объекты. Если в документе выбран один элемент, а в регистре записан другой, равенство полей в запросе ложно, и строка исключается из итоговой выборки.
Для диагностики этой проблемы разберем пошаговый алгоритм проверки:
ВНУТРЕННЕЕ СОЕДИНЕНИЕ на ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ.ЛЕВОМ СОЕДИНЕНИИ вы увидите данные документа, но поля регистра будут содержать NULL. При ПОЛНОМ СОЕДИНЕНИИ записи из документа и регистра расположатся в разных строках.титПутевойЛист.Водитель.Код и ТарифыЗПСрезПоследних.Сотрудник.Код, либо поля Ссылка. Вы обнаружите, что коды или физические элементы различаются.
// Диагностический запрос с полным соединением и выводом кодов
ВЫБРАТЬ
титПутевойЛист.Ссылка КАК Документ,
титПутевойЛист.Водитель КАК ВодительИзДокумента,
титПутевойЛист.Водитель.Код КАК КодВодителя,
ТарифыЗПСрезПоследних.Сотрудник КАК СотрудникИзРегистра,
ТарифыЗПСрезПоследних.Сотрудник.Код КАК КодСотрудника,
ТарифыЗПСрезПоследних.Сумма КАК Сумма
ИЗ
Документ.ПутевойЛист КАК титПутевойЛист
ПОЛНОЕ СОЕДИНЕНИЕ РегистрСведений.ТарифыЗП.СрезПоследних КАК ТарифыЗПСрезПоследних
ПО титПутевойЛист.Водитель = ТарифыЗПСрезПоследних.Сотрудник
Вторая важная ошибка в исходном запросе — обращение к виртуальной таблице РегистрСведений.ТарифыЗП.СрезПоследних без указания периода. В этом случае 1С возвращает срез данных, актуальный на текущую дату и время (момент выполнения запроса).
Если документ был выписан месяц назад, а тариф сотрудника с тех пор изменился или был введен задним числом, данные на текущий момент могут либо отсутствовать, либо быть неактуальными для периода документа. Методически правильно получать срез регистра на дату проведения документа (или на дату самого документа).
Соединение таблицы документов с виртуальными таблицами регистра напрямую в секции ПО — это критическая ошибка с точки зрения производительности СУБД.
При прямолинейном соединении платформа 1С вынуждена сначала скомпоновать таблицу СрезПоследних **по всем сотрудникам** за всю историю базы данных, и только потом сопоставлять ее с документами. На больших объемах данных это приводит к длительным блокировкам и зависанию запроса.
Проанализируем правильную методику построения такого запроса согласно стандартам разработки 1С:
Рассмотрим пример правильного запроса с использованием временных таблиц:
// Шаг 1. Выбираем данные документов во временную таблицу
ВЫБРАТЬ
ПутевойЛист.Ссылка КАК Документ,
ПутевойЛист.Дата КАК Дата,
ПутевойЛист.ЧасыВРаботе КАК ЧасыВРаботе,
ПутевойЛист.Водитель КАК Водитель
ПОМЕСТИТЬ ВТ_ПутевыеЛисты
ИЗ
Документ.ПутевойЛист КАК ПутевойЛист
ГДЕ
ПутевойЛист.Проведен = ИСТИНА
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2. Выбираем уникальных водителей для передачи в параметр виртуальной таблицы
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ.Водитель КАК Водитель
ПОМЕСТИТЬ ВТ_Водители
ИЗ
ВТ_ПутевыеЛисты КАК ВТ
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 3. Соединяем документы со срезом последних, ограниченным нужными сотрудниками
ВЫБРАТЬ
ВТ.Документ КАК Документ,
ВТ.ЧасыВРаботе КАК ЧасыВРаботе,
ВТ.Водитель КАК Водитель,
ТарифыЗПСрезПоследних.Тариф КАК Тариф,
ТарифыЗПСрезПоследних.Сумма КАК Сумма
ИЗ
ВТ_ПутевыеЛисты КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ТарифыЗП.СрезПоследних(
&Период,
Сотрудник В
(ВЫБРАТЬ
ВТ_Водители.Водитель
ИЗ
ВТ_Водители КАК ВТ_Водители)) КАК ТарифыЗПСрезПоследних
ПО ВТ.Водитель = ТарифыЗПСрезПоследних.Сотрудник
РАЗРЕШЕННЫЕВ тексте запроса используется инструкция ВЫБРАТЬ РАЗРЕШЕННЫЕ. Если у пользователя настроено ограничение прав на уровне записей (RLS) и нет прав на чтение конкретных записей регистра сведений ТарифыЗП, платформа не выдаст ошибку доступа.
Вместо ошибки запрос просто исключит «запрещенные» строки из результата при ВНУТРЕННЕМ СОЕДИНЕНИИ или подставит NULL при ЛЕВОМ СОЕДИНЕНИИ. Чтобы исключить влияние RLS при отладке, обязательно тестируйте выполнение запроса под пользователем с полными правами (Администратор).
ВЫРАЗИТЬ)Если реквизит Водитель в документе имеет составной тип данных (например, может принимать значения справочников Сотрудники и ФизическиеЛица), СУБД не сможет эффективно использовать индексы при соединении полей.
В таких случаях необходимо явно приводить тип поля с помощью функции ВЫРАЗИТЬ:
ПО ВЫРАЗИТЬ(ВТ.Водитель КАК Справочник.Сотрудники) = ТарифыЗПСрезПоследних.Сотрудник
Если запрос между документом и регистром сведений возвращает пустую таблицу, проанализируйте ситуацию по следующим шагам:
ПОЛНОЕ СОЕДИНЕНИЕ.ВЫРАЗИТЬ.