Разработчики 1С часто сталкиваются с ситуацией, когда один и тот же запрос, выполненный в консоли запросов и в Системе компоновки данных (СКД), выдает разные результаты. Особенно остро эта проблема проявляется при работе с виртуальными таблицами регистров накопления, такими как Продажи.Обороты или ТоварыНаСкладах.ОстаткиИОбороты. Давайте разберем, почему это происходит, проанализируем ошибки в представленном коде и выясним, как заставить СКД считать данные корректно.
Главное, что нам нужно понять: СКД — это не просто «пускатель» запроса. Это мощный генератор, который переписывает ваш текст запроса перед отправкой на SQL-сервер. Когда мы пишем запрос в наборе данных, СКД анализирует выбранные поля, настройки группировок и отборы пользователя, а затем оптимизирует код под эти нужды.
Если в обычном запросе параметры виртуальных таблиц (&НачалоПериода, &КонецПериода) передаются жестко, то СКД может игнорировать их, подставляя свои значения из стандартного периода. Рассмотрим пример из сообщения автора:
ВЫБРАТЬ
ПродажиОбороты.КоличествоОборот,
ПродажиОбороты.Номенклатура,
ПродажиОбороты.Период
ИЗ
РегистрНакопления.Продажи.Обороты(НАЧАЛОПЕРИОДА(&НачалоПериода, МЕСЯЦ), КОНЕЦПЕРИОДА(&КонецПериода, МЕСЯЦ), Месяц, ) КАК ПродажиОбороты
Проанализируем ситуацию: если в настройках СКД пользователь установит другой период или наложит отбор по конкретной номенклатуре, компоновщик может вынести эти отборы внутрь виртуальной таблицы. Если связи между временными таблицами настроены неверно, это приведет к потере или дублированию данных.
В тексте темы мы видим левое соединение таблицы продаж за месяц с таблицей остатков по дням. Это классическая «ловушка» для ресурсов СКД. Давайте посмотрим на это соединение:
ИЗ ПродажиТоваров КАК ПродажиТоваров
ЛЕВОЕ СОЕДИНЕНИЕ ИтоговыеОстатки КАК ИтоговыеОстатки
ПО ПродажиТоваров.Номенклатура = ИтоговыеОстатки.Номенклатура
И (ПродажиТоваров.Период = НАЧАЛОПЕРИОДА(ИтоговыеОстатки.Период, МЕСЯЦ))
Разберем, что происходит: на одну запись о продажах за месяц (например, январь) приходится 31 запись из таблицы остатков (на каждый день января). Когда СКД начинает рассчитывать ресурс СУММА(КоличествоОборот), она видит в результирующей выборке 31 строку. В итоге сумма продаж будет завышена в 31 раз! Чтобы избежать этого, в настройках ресурсов СКД для полей, которые могут дублироваться из-за соединений «один ко многим», следует использовать функции МАКСИМУМ или СРЕДНЕЕ вместо простого суммирования, либо правильно настраивать роли полей.
В сообщении №5 форума было сделано важное замечание: «сплошное разыменование — тебя админ убьет за такой запрос». Посмотрим на этот фрагмент:
СГРУППИРОВАТЬ ПО
Дни.ДатаКалендаря,
ТоварыНаСкладахОстаткиИОбороты.Номенклатура,
ТоварыНаСкладахОстаткиИОбороты.Номенклатура.Артикул
Обращение Номенклатура.Артикул внутри запроса (особенно внутри виртуальных таблиц или группировок) заставляет систему выполнять неявное левое соединение с таблицей справочника. Если таких обращений много, SQL-запрос превращается в «монстра» из десятков соединений. В СКД это усугубляется тем, что система сама добавляет соединения для получения представлений объектов. Рекомендация: всегда выбирайте только ссылку (Номенклатура), а дополнительные поля (Артикул, Производитель) выводите уже в настройках выбранных полей СКД — система сама их эффективно подтянет.
Выясним причину расхождений самым надежным способом. Как советуют в сообщении №2, нам поможет Консоль компоновки данных. Процесс диагностики выглядит так:
Скорее всего, вы увидите, что СКД добавила свои условия в секции ГДЕ или изменила параметры виртуальных таблиц, что и привело к иному результату.
Для корректного расчета остатков на каждый день вместе с оборотами за период, рекомендуем пересмотреть архитектуру запроса. Вместо того чтобы вручную соединять данные с производственным календарем (как в коде автора), лучше использовать механизм «Дополнения периодов» в СКД.
Проанализируем шаги по исправлению структуры:
Период укажите роль «Период», для остатка — роль «Остаток» (с указанием типа: Начальный или Конечный).{&НачалоПериода}. Это жестко закрепит ваше условие.Посмотрим, как можно оптимизировать получение данных по остаткам без лишних ВЫБОР КОГДА:
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоКонечныйОстаток КАК Остаток,
Остатки.Период КАК Период
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК Остатки
Такой подход позволит СКД корректно интерпретировать данные. Помните, что виртуальные таблицы — это не физические таблицы на диске, а динамические выборки, которые СУБД строит «на лету» (сообщения 9-11). Чем проще и прозрачнее будет ваш исходный запрос, тем меньше вероятность, что компоновщик при трансформации допустит логическую ошибку.