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