При переходе на использование прямых SQL-запросов (с использованием внешних компонент 1С++ и консоли запросов) разработчики часто сталкиваются с ошибкой парсера метаимен вида qryConsole : Meta name parser error: неизвестное метаимя или алиас "$Продажи". Это происходит при попытке передать параметры периода в круглых скобках непосредственно в физическую таблицу регистра.
В этой статье мы подробно разберем, почему возникает данная ошибка, как правильно накладывать условия на период в физических таблицах движений, а также рассмотрим оптимизацию запросов и альтернативный вариант с виртуальными таблицами оборотов.
Проанализируем структуру обращения к метаданным в 1С++. В прямых запросах используются метаимена двух принципиально разных типов:
$Регистр.ИмяРегистра транслируется парсером в реальную таблицу движений базы данных SQL (таблицу с префиксом RA). Физическая таблица не является функцией и не может принимать параметры периода в круглых скобках. Попытка написать $Регистр.Продажи(:НачДата, :КонДата) приводит к синтаксической ошибке.$РегистрОбороты.ИмяРегистра, $РегистрОстатки.ИмяРегистра и $РегистрОстаткиОбороты.ИмяРегистра являются макроподстановками 1С++, которые генерируют вложенные подзапросы к таблицам итогов и движений. Только они принимают параметры периода и измерений в круглых скобках.Если в отчете требуется выборка по физической таблице движений регистра (например, для детализации по документам или строкам), ограничение по периоду накладывается классическим предложением WHERE с использованием оператора BETWEEN.
Посмотрим на пример правильного формирования текста прямого запроса:
ТекстЗапроса = "
|SELECT
| ($НачалоПериода.День(Продажи.DATE_TIME_IDDOC)) Период
| , $Продажи.Фирма [Фирма $Справочник.Фирмы]
| , $Продажи.Поставщик [Поставщик $Справочник.Контрагенты]
| , $Продажи.Покупатель [Покупатель $Справочник.Контрагенты]
| , $Продажи.Номенклатура [Номенклатура $Справочник.Номенклатура]
| , $Продажи.ПродСтоимость ПродСтоимость
| , $Продажи.Себестоимость Себестоимость
| , $Продажи.Количество Количество
|FROM $Регистр.Продажи AS Продажи
|WHERE Продажи.DATE_TIME_IDDOC BETWEEN :ДатаНач AND :ДатаКон~
|TOTALS Sum(ПродСтоимость)
| , Sum(Себестоимость)
| , Sum(Количество)
|BY ALL
| , Период PERIODS
| , [Фирма $Справочник.Фирмы]
| , [Поставщик $Справочник.Контрагенты] HIERARCHY
| , [Покупатель $Справочник.Контрагенты] HIERARCHY
| , [Номенклатура $Справочник.Номенклатура] HIERARCHY ONLY
|";
Передадим параметры в объект выполнения запроса на встроенном языке 1С 7.7:
Консоль.УстановитьПараметр("ДатаНач", ВыбНачПериода);
Консоль.УстановитьПараметр("ДатаКон", ВыбКонПериода);
Разберем по шагам ключевые детали приведенного решения:
~): В СУБД 1С 7.7 поле DATE_TIME_IDDOC хранит строку длиной 23 символа (дата, время и идентификатор документа). Указание тильды в параметре :ДатаКон~ сообщает парсеру 1С++, что дату необходимо развернуть не на начало дня (00:00:00), а на максимальное строковое значение конца суток (23:59:59 и максимальный IDDOC). Без тильды документы за дату окончания периода не попадут в отчет.УстановитьПараметр вместо УстановитьТекстовыйПараметр, так как он автоматически преобразует тип Дата платформы 1С во внутренний формат СУБД YYYYMMDD._1SJOURN: В физической таблице движений регистра (RA...) уже присутствует колонка DATE_TIME_IDDOC. Если вам не нужны дополнительные реквизиты общего журнала (номер документа, автор, признак проведенности и т.д.), соединение INNER JOIN _1SJOURN выполнять не требуется. Наложение условия напрямую на Продажи.DATE_TIME_IDDOC снижает нагрузку на SQL-сервер._1SJOURN все же используется, следует исключать отмененные и непроведенные документы. Для этого в блок WHERE добавляется условие на битовую маску флага проведения: (Журнал.CLOSED & 1) = 1.$НачалоПериода и секции PERIODS: Конструкция ($НачалоПериода.День(Продажи.DATE_TIME_IDDOC)) Период в секции SELECT и строка Период PERIODS в блоке TOTALS не ограничивают выборку по датам. Они служат для группировки данных по интервалам времени и автоматического заполнения пустых дней в итоговой таблице. Удалять эту строку не нужно, если в отчете требуется периодичность по дням.Если задача отчета заключается в получении сводных оборотов без детального анализа каждого отдельного движения, предпочтительно использовать виртуальную таблицу $РегистрОбороты. Это работает существенно быстрее за счет обращения к агрегированным итогам регистра.
Посмотрим на пример запроса с виртуальной таблицей оборотов:
ТекстЗапроса = "
|SELECT
| Продажи.ПериодДвижения [Период $Период.День]
| , Продажи.Фирма [Фирма $Справочник.Фирмы]
| , Продажи.Поставщик [Поставщик $Справочник.Контрагенты]
| , Продажи.Покупатель [Покупатель $Справочник.Контрагенты]
| , Продажи.Номенклатура [Номенклатура $Справочник.Номенклатура]
| , Продажи.ПродСтоимостьОборот ПродСтоимость
| , Продажи.СебестоимостьОборот Себестоимость
| , Продажи.КоличествоОборот Количество
|FROM $РегистрОбороты.Продажи(:ДатаНач, :ДатаКон~, День, , ,
| (Фирма, Поставщик, Покупатель, Номенклатура),
| (Количество, Себестоимость, ПродСтоимость)) AS Продажи
|TOTALS Sum(ПродСтоимость)
| , Sum(Себестоимость)
| , Sum(Количество)
|BY ALL
| , [Период $Период.День] PERIODS
| , [Фирма $Справочник.Фирмы]
| , [Поставщик $Справочник.Контрагенты] HIERARCHY
| , [Покупатель $Справочник.Контрагенты] HIERARCHY
| , [Номенклатура $Справочник.Номенклатура] HIERARCHY ONLY
|";
В этом случае параметры периода передаются непосредственно в параметры виртуальной таблицы в круглых скобках, а расчет оборотов оптимизируется встроенными механизмами 1С++.