Введение
В предыдущей статье по регистрам бухгалтерии "Регистры бухгалтерии. Настройки, субконто и движения с субконто" мы подробным образом рассмотрели работу настроек этого регистра, а также внутренности виртуальных таблиц "Субконто" и "Движения с субконто". Коснулись некоторых особенностей настроек и их влияние на хранение данных регистра на стороне базы данных.
Сегодня мы продолжим анализ работы регистра, но на этот раз рассмотрим только одну виртуальную таблицу "Обороты". Такая маленькая, и такая интересная!
Все данные для публикации получены с помощью инструментов:
* Транслятор запросов 1С
* Просмотр и анализ структуры базы данных (отчет на СКД)
* Помощник работы с идентификаторами
Начнем с простых запросов, которые генерирует платформа 1С, и закончим на особенностях и вопросах производительности.
Небольшое отступление
И так, начнем с простого. Все примеры продолжим делать на той же базе данных, что и в предыдущей статье.
Для чего нужна таблица оборотов? Правильно, для получения оборотов по счету (внезапно!). При этом можно получить оборот как общий по счету, так и в разрезе аналитики: измерений регистра (организация, валюта, подразделение и т.д.) или субконто счета. И, конечно же, можно получить обороты с учетом кор. счета и кор. аналитики по этому счету.
В общем, виртуальная таблица, часто используемая как в отчетах, так и в алгоритмах. Все, кто работает с учетными системами почти всегда сталкиваются с регистрами бухгалтерии и с таблицей оборотов, в частности. В самой знаменитой типовой конфигурации "Бухгалтерия предприятия" эта виртуальная таблица используется в таких отчетах как:
- Анализ состояния учета по налогу на прибыль
- Продажи
- Расшифровка платежей в бюджет
- И еще много-много всего.
Думаю, что необходимость использования этой виртуальной таблицы очевидна. Перейдем к первому примеру и рассмотрим, что именно делает платформа 1С с этой виртуальной таблицей.
Простой пример
Первый запрос имеет мало общего с практическими задачами (и я надеюсь Вы так не делаете на рабочем базе). Получим обороты для всех счетов по сумме и количеству.
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(, , , , , , , ) КАК ХозрасчетныйОбороты
Если Вы далаете такие запросы на рабочей базе, то стоит подумать над тем, чтобы от такого отказаться. Почему? Взгляните какой запрос мы имеем на стороне базы данных.
SELECT
T1.AccountRRef, -- Счет
T1.Fld790Turnover_, -- СуммаОборот
T1.Fld792Turnover_ -- КоличествоОборот
FROM (
SELECT
T2.AccountRRef AS AccountRRef, -- Счет
-- СуммаОборот
CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) END AS Fld790Turnover_,
-- КоличествоОборот
CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) END AS Fld792Turnover_
FROM (
--
SELECT
T3._AccountDtRRef AS AccountRRef, -- СчетДт
T3._Fld790 AS Fld790Turnover_, -- Сумма
T3._Fld792Dt AS Fld792Turnover_ -- КоличествоДт
FROM dbo._AccRg786 T3 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T3._Fld774 = @P1)) -- Разделитель данных
AND (T3._Active = 0x01 -- Активность
AND T3._AccountDtRRef <> @P2)
UNION ALL
SELECT
T4._AccountCtRRef AS AccountRRef, -- СчетКт
-T4._Fld790 AS Fld790Turnover_, -- Сумма
-T4._Fld792Ct AS Fld792Turnover_ -- КоличествоКт
FROM dbo._AccRg786 T4 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T4._Fld774 = @P3)) -- Разделитель данных
AND (T4._Active = 0x01 -- Активность
AND T4._AccountCtRRef <> @P4)
) T2
GROUP BY T2.AccountRRef -- Группировка по счету
-- Фильтруем итоговые записи, чтобы в них были данные либо по обороту суммы или количесества
HAVING (CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) END) <> 0.0
) T1
Как мы видим, платформа получает данные из основной таблицы регистра для получения оборотов по указанным ресурсам. Алгоритм такой:
- Получаем обороты из основной таблицы по дебету
- Получаем обороты из основной таблицы по кредиту
- Объединяем полученные данные в единую таблицу и группируем по счету
В принципе все работает логично. Но может появиться вопрос: почему платформа не использует данные итогов? Ведь оптимально было бы получить общие показатели из таблиц итогов, чем рассчитывать все заново по основной таблице. Ответ в этом случае прост - в запросе не установлен отбор по периоду. Чтобы платформа 1С начала использовать таблицы итогов нужно установить фильтр по периоду, как минимум по началу периода.
Например, если в базе установлен период рассчитанных итогов для регистра бухгалтерии с 01.01.2019 по 31.07.2020, то для начала использования итогов начало периода должно быть установлено больше или равным 01.01.2019. Есть нюанс: если поставить дату начала периода меньше даты рассчитанных итогов (но не более чем на 1 месяц), то платформа все равно будет использовать итоги, а для периода до минимальной даты рассчитанных итогов будет сделан запрос к основным таблицам регистра (точнее два запроса - для дебета и для кредита). Вот так стал выглядеть запрос с началом периода.
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(&НачалоПериода, , , , , , , ) КАК ХозрасчетныйОбороты
Чтобы пример был более полным, установим параметр "НачалоПериода" как 10.12.2018, т.е. меньше минимальной даты рассчитанных итогов, но не более чем на 1 месяц. Вот такой запрос будет сформирован к базе данных.
SELECT
T1.AccountRRef, -- Счет
T1.Fld790Turnover_, -- СуммаОборот
T1.Fld792Turnover_ -- КоличествоОборот
FROM (SELECT
T2.AccountRRef AS AccountRRef, -- Счет
-- СуммаОборот
CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) END AS Fld790Turnover_,
-- КоличествоОборот
CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) END AS Fld792Turnover_
FROM (
-- 1. Получаем данные по оборотам из таблицы итогов "Итоги по счетам" за период
-- с начала даты рассчитанных итогов до даты рассчитанных итогов
SELECT
T3._AccountRRef AS AccountRRef, -- Счет
-- СуммаОборотДт - СуммаОборотКт = СуммаОборот
T3._TurnoverDt801 - T3._TurnoverCt802 AS Fld790Turnover_,
-- КоличествоОборотДт - КоличествоОборотКт = КоличествоОборот
T3._TurnoverDt807 - T3._TurnoverCt808 AS Fld792Turnover_
FROM dbo._AccRgAT0800 T3 -- РегистрБухгалтерии.Хозрасчетный.ИтогиПоСчетам
WHERE ((T3._Fld774 = @P1)) -- Разделитель данных
AND (
-- Фильтр по периоду
T3._Period >= @P2 -- '4019-01-01 00:00:00'
AND T3._Period < @P3 -- '4020-08-01 00:00:00'
-- Убираем значения с 0 показателями
AND (T3._TurnoverDt801 <> @P4
OR T3._TurnoverCt802 <> @P5
OR T3._TurnoverDt807 <> @P6
OR T3._TurnoverCt808 <> @P7)
)
UNION ALL
-- 2. Получаем данные по оборотам Дт из основной таблицы регистра в период
-- с 10.12.2018 по 31.12.2018. В этом периоде итоги не были рассчитаны
SELECT
T4._AccountDtRRef AS AccountRRef, -- Счет
CAST(T4._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_, -- СуммаОборот
CAST(T4._Fld792Dt AS NUMERIC(22, 3)) AS Fld792Turnover_ -- КоличествоОборот
FROM dbo._AccRg786 T4 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T4._Fld774 = @P8)) -- Разделитель данных
AND (T4._Active = 0x01 -- Активность
AND T4._AccountDtRRef <> @P9 -- СчетДт не пустой
-- Фильтр по периоду
AND T4._Period >= @P10 -- '4018-12-10 00:00:00'
AND T4._Period < @P11) -- '4019-01-01 00:00:00'
UNION ALL
-- 2. Получаем данные по оборотам Кт из основной таблицы регистра в период
-- с 10.12.2018 по 31.12.2018. В этом периоде итоги не были рассчитаны
SELECT
T5._AccountCtRRef AS AccountRRef, -- Счет
CAST(-T5._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_, -- СуммаОборот
CAST(-T5._Fld792Ct AS NUMERIC(22, 3)) AS Fld792Turnover_ -- КоличествоОборот
FROM dbo._AccRg786 T5 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T5._Fld774 = @P12)) -- Разделитель данных
AND (T5._Active = 0x01 -- Активность
AND T5._AccountCtRRef <> @P13 -- СчетКт не пустой
AND T5._Period >= @P14 -- '4018-12-10 00:00:00'
AND T5._Period < @P15) -- '4019-01-01 00:00:00'
UNION ALL
-- 3. Получаем данные по оборотам Дт по записям с 01.08.2020, т.к. в этот период
-- итоги еще не рассчитывались
SELECT
T6._AccountDtRRef AS AccountRRef, -- Счет
CAST(T6._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_, -- СуммаОборот
CAST(T6._Fld792Dt AS NUMERIC(22, 3)) AS Fld792Turnover_ -- КоличествоОборот
FROM dbo._AccRg786 T6 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T6._Fld774 = @P16)) -- Разделитель данных
AND (T6._Active = 0x01 -- Активность
AND T6._AccountDtRRef <> @P17 -- СчетДт не пустой
AND T6._Period >= @P18) -- '4020-08-01 00:00:00'
UNION ALL
-- 4. Получаем данные по оборотам Кт по записям с 01.08.2020, т.к. в этот период
-- итоги еще не рассчитывались
SELECT
T7._AccountCtRRef AS AccountRRef, -- Счет
CAST(-T7._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_, -- СуммаОборот
CAST(-T7._Fld792Ct AS NUMERIC(22, 3)) AS Fld792Turnover_ -- КоличествоОборот
FROM dbo._AccRg786 T7 -- РегистрБухгалтерии.Хозрасчетный.Основная
WHERE ((T7._Fld774 = @P19)) -- Разделитель данных
AND (T7._Active = 0x01 -- Активность
AND T7._AccountCtRRef <> @P20 -- СчетКт не пустой
AND T7._Period >= @P21) -- '4020-08-01 00:00:00'
) T2
GROUP BY T2.AccountRRef -- Группировка по счету
-- Фильтруем итоговые записи, чтобы в них были данные либо по обороту суммы или количесества
HAVING (CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) END) <> 0.0
) T1
В чем-то этот запрос похож на предыдущий пример, но значительные отличия все же присутствуют:
- В первую очередь данные за тот период, в котором есть рассчитанные итоги, получаются именно из таблицы итогов. В нашем случаев используются итоги между счетами.
- Там, где итоги недоступны платформа 1С собирает данные из основных таблиц регистра (период до расчета итогов и после максимальной даты рассчитанных итогов).
Ранее в одной из статей мы говорили о том, почему рассчитывать итоги очень важно. И снова на примере выше было показано, что если итоги не обслуживаются, то запросы к регистру бухгалтерии будут работать менее эффективно. Давайте перейдем к более сложному примеру.
А если добавить субконто
Настало время усложнить пример и добавить в запрос субконто, отбор по счету и измерению "Организация".
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК Субконто1,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоОборот,
ХозрасчетныйОбороты.КоличествоОборотДт КАК КоличествоОборотДт,
ХозрасчетныйОбороты.КоличествоОборотКт КАК КоличествоОборотКт,
ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот,
ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаОборотДт,
ХозрасчетныйОбороты.СуммаОборотКт КАК СуммаОборотКт
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачалоПериода,
&КонецПериода,
,
Счет = &Счет,
&ВидСубконто,
Организация = &Организация
И Субконто1 = &Субконто1,
,
) КАК ХозрасчетныйОбороты
Намешали сразу почти все, что можно в этой виртуальной таблице, кроме кор. счета и аналитики с ресурсами по нему, т.к. об этом позже. Вот какой запрос, а точнее серию запросов, в этом случае сформирует платформа 1С.
-- Получаем информацию о настройках счетов, используемых параметрах виртуальных таблиц
-- Настройки получаем с отбором по виду субконто и счету, т.к. у нас именно такие отборы
SELECT
T1._Acc22_IDRRef, -- Ссылка на счет
T1._Fld782, -- Количественный
T1._Fld780 -- Суммовой
FROM dbo._Acc22_ExtDim779 T1 -- ПланСчетов.Хозрасчетный.ТабличнаяЧасть.ВидыСубконто
WHERE ((T1._Fld774 = @P1)) -- Разделитель данных
AND ((T1._DimKindRRef = @P2) -- ВидСубконто
AND ((T1._Acc22_IDRRef = @P3))) -- Ссылка на счет
-- Получаем дополнительные сведения о счете
INSERT INTO #tt13 WITH(TABLOCK)
(
_IDRRef, -- Ссылка на счет
_LineNo1, -- Номер строки в таблице субконто (по факту номер субконто)
_Cnt -- Количество видов субконто
)
SELECT
T1._IDRRef, -- Ссылка на счет
T2._LineNo, -- Номер строки
CAST(COUNT_BIG(T3._Acc22_IDRRef) AS NUMERIC(12)) -- Количество субконто
FROM dbo._Acc22 T1 -- ПланСчетов.Хозрасчетный
INNER JOIN dbo._Acc22_ExtDim779 T2 -- ПланСчетов.Хозрасчетный.ТабличнаяЧасть.ВидыСубконто
ON T2._Acc22_IDRRef = T1._IDRRef -- Условие по ссылке
AND T2._DimKindRRef = @P1 -- Вид субконто
AND T2._Fld774 = T1._Fld774 -- Разделитель данных
LEFT OUTER JOIN dbo._Acc22_ExtDim779 T3 -- ПланСчетов.Хозрасчетный.ТабличнаяЧасть.ВидыСубконто
ON T3._Acc22_IDRRef = T1._IDRRef -- Условие по ссылке
AND T3._Fld774 = T1._Fld774 -- Разделитель данных
WHERE ((T1._Fld774 = @P2)) -- Разделитель данных
AND ((T1._IDRRef = @P3)) -- Ссылка на счет
GROUP BY
T1._IDRRef,
T2._LineNo
SELECT DISTINCT
T1._Cnt,
T1._LineNo1
FROM #tt13 T1 WITH(NOLOCK)
-- Основной запрос получения данных
SELECT
T1.AccountRRef, -- Счет
T1.Value1_TYPE, -- Субконто (вид значения)
T1.Value1_RTRef,
T1.Value1_RRRef,
T1.Fld792Turnover_,
T1.Fld792TurnoverDt_,
T1.Fld792TurnoverCt_,
T1.Fld790Turnover_,
T1.Fld790TurnoverDt_,
T1.Fld790TurnoverCt_
FROM (
SELECT
T2.AccountRRef AS AccountRRef, -- Счет
T2.Value1_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T2.Value1_RTRef AS Value1_RTRef,
T2.Value1_RRRef AS Value1_RRRef,
CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) END AS Fld790Turnover_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(27, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(27, 2)) END AS Fld790TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(27, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(27, 2)) END AS Fld790TurnoverCt_,
CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) END AS Fld792Turnover_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(27, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(27, 3)) END AS Fld792TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(27, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(27, 3)) END AS Fld792TurnoverCt_
FROM (
-- 1. Получаем данные оборотов из таблицы итогов по субконто
SELECT
T3._AccountRRef AS AccountRRef, -- Счет
T3._Value1_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T3._Value1_RTRef AS Value1_RTRef, -- Субконто (тип ссылки)
T3._Value1_RRRef AS Value1_RRRef, -- Субконто (значение ссылки)
-- СуммаОборотДт - СуммаОборотКт = СуммаОборот
T3._TurnoverDt801 - T3._TurnoverCt802 AS Fld790Turnover_,
T3._TurnoverDt801 AS Fld790TurnoverDt_, -- СуммаОборотДт
T3._TurnoverCt802 AS Fld790TurnoverCt_, -- СуммаОборотКт
-- КоличествоОборотДт - КоличествоОборотКт = КоличествоОборот
T3._TurnoverDt807 - T3._TurnoverCt808 AS Fld792Turnover_,
T3._TurnoverDt807 AS Fld792TurnoverDt_, -- КоличествоОборотДт
T3._TurnoverCt808 AS Fld792TurnoverCt_ -- КоличествоОборотКт
FROM dbo._AccRgAT2820 T3 -- РегистрБухгалтерии.Хозрасчетный.ИтогиПоСчетамССубконто2
-- Внутренним соединением отбираем записи с отбором по счету и виду субконто
INNER JOIN #tt13 T4 WITH(NOLOCK)
ON T4._IDRRef = T3._AccountRRef AND T4._LineNo1 = @P1 AND T4._Cnt = @P2
WHERE ((T3._Fld774 = @P3)) -- Разделитель данных
AND (((T3._Fld787RRef = @P4) -- Организация
AND (T3._Value1_TYPE = 0x08 -- Субконто (вид значения)
AND T3._Value1_RTRef = 0x0000001A -- Субконто (тип ссылки)
AND T3._Value1_RRRef = @P5)) -- Субконто (значение ссылки)
AND T3._Period >= @P6 -- Начало периода
AND T3._Period < @P7 -- Конец периода
-- Должно быть хотя бы одно значение ресурса заполненным
AND (T3._TurnoverDt801 <> @P8
OR T3._TurnoverCt802 <> @P9
OR T3._TurnoverDt807 <> @P10
OR T3._TurnoverCt808 <> @P11))
UNION ALL
-- 2. Получаем данные из основных таблиц регистра за период,
-- когда итоги еще не были рассчитаны.
-- В данном случае обороты по дебету
SELECT
T5._AccountDtRRef AS AccountRRef, -- Счет
T6._Value_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T6._Value_RTRef AS Value1_RTRef, -- Субконто (тип ссылки)
T6._Value_RRRef AS Value1_RRRef, -- Субконто (значение ссылки)
CAST(T5._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_,
CAST(T5._Fld790 AS NUMERIC(21, 2)) AS Fld790TurnoverDt_,
CAST(@P12 AS NUMERIC(21, 2)) AS Fld790TurnoverCt_,
CAST(T5._Fld792Dt AS NUMERIC(22, 3)) AS Fld792Turnover_,
CAST(T5._Fld792Dt AS NUMERIC(21, 3)) AS Fld792TurnoverDt_,
CAST(CASE WHEN T5._Fld792Dt IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P13 END AS NUMERIC(21, 3)) AS Fld792TurnoverCt_
FROM dbo._AccRg786 T5 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T6 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T6._RecorderTRef = T5._RecorderTRef
AND T6._RecorderRRef = T5._RecorderRRef
AND T6._LineNo = T5._LineNo -- Номер строки
AND T6._Period = T5._Period -- Период
AND T6._Correspond = @P14 -- Корреспонденция
AND T6._KindRRef = @P15 -- Вид субконто
-- Отбор по периоду с '4018-12-10 00:00:00' по '4019-01-01 00:00:00'
AND T6._Period >= @P16 -- Начало периода
AND T6._Period < @P17 -- Конец периода
WHERE (((T5._Fld774 = @P18)) -- Разделитель данных
AND (T6._Fld774 = @P19)) -- Разделитель данных
AND (T5._Active = 0x01 -- Активность
AND T5._AccountDtRRef <> @P20 -- Счет не пустой
AND ((T5._AccountDtRRef = @P21)) -- СчетДт
AND ((T5._Fld787RRef = @P22) -- Организация
AND (T6._Value_TYPE = 0x08 -- Субконто (вид значения)
AND T6._Value_RTRef = 0x0000001A -- Субконто (тип ссылки)
AND T6._Value_RRRef = @P23)) -- Субконто (значение ссылки)
-- Отбор по периоду с '4018-12-10 00:00:00' по '4019-01-01 00:00:00'
AND T5._Period >= @P24 -- Начало периода
AND T5._Period < @P25) -- Конец периода
UNION ALL
-- 3. Получаем данные из основных таблиц регистра за период,
-- когда итоги еще не были рассчитаны.
-- В данном случае обороты по кредиту
SELECT
T7._AccountCtRRef AS AccountRRef, -- Счет
T8._Value_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T8._Value_RTRef AS Value1_RTRef, -- Субконто (тип ссылки)
T8._Value_RRRef AS Value1_RRRef, -- Субконто (значение ссылки)
CAST(-T7._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_,
CAST(@P26 AS NUMERIC(21, 2)) AS Fld790TurnoverDt_,
CAST(T7._Fld790 AS NUMERIC(21, 2)) AS Fld790TurnoverCt_,
CAST(-T7._Fld792Ct AS NUMERIC(22, 3)) AS Fld792Turnover_,
CAST(CASE WHEN T7._Fld792Ct IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P27 END AS NUMERIC(21, 3)) AS Fld792TurnoverDt_,
CAST(T7._Fld792Ct AS NUMERIC(21, 3)) AS Fld792TurnoverCt_
FROM dbo._AccRg786 T7 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T8 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T8._RecorderTRef = T7._RecorderTRef -- Регистратор, тип ссылки
AND T8._RecorderRRef = T7._RecorderRRef -- Регистратор, ссылка
AND T8._LineNo = T7._LineNo -- Номер строки
AND T8._Period = T7._Period -- Период
AND T8._Correspond = @P28 -- Корреспонденция
AND T8._KindRRef = @P29 -- Вид субконто
-- Отбор по периоду с '4018-12-10 00:00:00' по '4019-01-01 00:00:00'
AND T8._Period >= @P30 -- Начало периода
AND T8._Period < @P31 -- Конец периода
WHERE (((T7._Fld774 = @P32)) -- Разделитель данных
AND (T8._Fld774 = @P33)) -- Разделитель данных
AND (T7._Active = 0x01 -- Активность
AND T7._AccountCtRRef <> @P34 -- Счет не пустой
AND ((T7._AccountCtRRef = @P35)) -- СчетКт
AND ((T7._Fld787RRef = @P36) -- Организация
AND (T8._Value_TYPE = 0x08 -- Субконто (вид значения)
AND T8._Value_RTRef = 0x0000001A -- Субконто (тип ссылки)
AND T8._Value_RRRef = @P37)) -- Субконто (значение ссылки)
-- Отбор по периоду с '4018-12-10 00:00:00' по '4019-01-01 00:00:00'
AND T7._Period >= @P38 -- Начало периода
AND T7._Period < @P39) -- Конец периода
UNION ALL
-- 4. Далее по основным таблицам регистра получаем данные за период вне рассчитанных итогов по дебету
-- В данном случае это за 01.03.2020, т.к. итоги были получены за январь и февраль, т.к. месяцы полностью входят в период
-- А за отдельный день 1 марта итогов нет, т.к. итоги рассчитываются за месяц, поэтому приходится использовать основные таблицы
SELECT
T9._AccountDtRRef AS AccountRRef, -- Счет
T10._Value_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T10._Value_RTRef AS Value1_RTRef, -- Субконто (тип ссылки)
T10._Value_RRRef AS Value1_RRRef, -- Субконто (значение ссылки)
CAST(T9._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_,
CAST(T9._Fld790 AS NUMERIC(21, 2)) AS Fld790TurnoverDt_,
CAST(@P40 AS NUMERIC(21, 2)) AS Fld790TurnoverCt_,
CAST(T9._Fld792Dt AS NUMERIC(22, 3)) AS Fld792Turnover_,
CAST(T9._Fld792Dt AS NUMERIC(21, 3)) AS Fld792TurnoverDt_,
CAST(CASE WHEN T9._Fld792Dt IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P41 END AS NUMERIC(21, 3)) AS Fld792TurnoverCt_
FROM dbo._AccRg786 T9 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T10 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T10._RecorderTRef = T9._RecorderTRef
AND T10._RecorderRRef = T9._RecorderRRef
AND T10._LineNo = T9._LineNo
AND T10._Period = T9._Period
AND T10._Correspond = @P42
AND T10._KindRRef = @P43
-- Отбор по периоду '4020-03-01 00:00:00' по '4020-03-01 00:00:00'
AND T10._Period >= @P44
AND T10._Period <= @P45
WHERE (((T9._Fld774 = @P46)) -- Разделитель данных
AND (T10._Fld774 = @P47)) -- Разделитель данных
AND (T9._Active = 0x01 -- Активность
AND T9._AccountDtRRef <> @P48 -- Счет не пустой
AND ((T9._AccountDtRRef = @P49)) -- СчетДт
AND ((T9._Fld787RRef = @P50) -- Организация
AND (T10._Value_TYPE = 0x08 -- Субконто (вид значения)
AND T10._Value_RTRef = 0x0000001A -- Субконто (тип ссылки)
AND T10._Value_RRRef = @P51)) -- Субконто (значение ссылки)
-- Отбор по периоду '4020-03-01 00:00:00' по '4020-03-01 00:00:00'
AND T9._Period >= @P52 -- Начало периода
AND T9._Period <= @P53) -- Окончание периода
UNION ALL
-- 5. Далее по основным таблицам регистра получаем данные за период вне рассчитанных итогов по оборотам кредита
-- В данном случае это за 01.03.2020, т.к. итоги были получены за январь и февраль, т.к. месяцы полностью входят в период
-- А за отдельный день 1 марта итогов нет, т.к. итоги рассчитываются за месяц, поэтому приходится использовать основные таблицы
SELECT
T11._AccountCtRRef AS AccountRRef, -- Счет
T12._Value_TYPE AS Value1_TYPE, -- Субконто (вид значения)
T12._Value_RTRef AS Value1_RTRef, -- Субконто (тип ссылки)
T12._Value_RRRef AS Value1_RRRef, -- Субконто (значение ссылки)
CAST(-T11._Fld790 AS NUMERIC(22, 2)) AS Fld790Turnover_,
CAST(@P54 AS NUMERIC(21, 2)) AS Fld790TurnoverDt_,
CAST(T11._Fld790 AS NUMERIC(21, 2)) AS Fld790TurnoverCt_,
CAST(-T11._Fld792Ct AS NUMERIC(22, 3)) AS Fld792Turnover_,
CAST(CASE WHEN T11._Fld792Ct IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P55 END AS NUMERIC(21, 3)) AS Fld792TurnoverDt_,
CAST(T11._Fld792Ct AS NUMERIC(21, 3)) AS Fld792TurnoverCt_
FROM dbo._AccRg786 T11 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T12 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T12._RecorderTRef = T11._RecorderTRef
AND T12._RecorderRRef = T11._RecorderRRef
AND T12._LineNo = T11._LineNo -- Номер строки
AND T12._Period = T11._Period -- Период
AND T12._Correspond = @P56 -- Корреспонденция
AND T12._KindRRef = @P57 -- Вид субконто
-- Отбор по периоду '4020-03-01 00:00:00' по '4020-03-01 00:00:00'
AND T12._Period >= @P58 -- Начало периода
AND T12._Period <= @P59 -- Конец периода
WHERE (((T11._Fld774 = @P60)) -- Разделитель данных
AND (T12._Fld774 = @P61)) -- Разделитель данных
AND (T11._Active = 0x01 -- Активность
AND T11._AccountCtRRef <> @P62 -- Счет не пустой
AND ((T11._AccountCtRRef = @P63)) -- СчетКт
AND ((T11._Fld787RRef = @P64) -- Организация
AND (T12._Value_TYPE = 0x08 -- Субконто (вид значения)
AND T12._Value_RTRef = 0x0000001A -- Субконто (тип ссылки)
AND T12._Value_RRRef = @P65)) -- Субконто (значение ссылки)
-- Отбор по периоду '4020-03-01 00:00:00' по '4020-03-01 00:00:00'
AND T11._Period >= @P66 -- Начало периода
AND T11._Period <= @P67) -- Конец периода
) T2
GROUP BY
T2.AccountRRef, -- Счет
T2.Value1_TYPE, -- Субконто (вид значения)
T2.Value1_RTRef, -- Субконто (тип ссылки)
T2.Value1_RRRef -- Субконто (значение ссылки)
-- Фильтруем результат. Получаем только те записи, для которых есть данные хотя бы по одному ресурсу
HAVING (CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(28, 2)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(27, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(27, 2)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(27, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(27, 2)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(28, 3)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(27, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(27, 3)) END) <> 0.0
OR (CASE WHEN CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(27, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(27, 3)) END) <> 0.0) T1
-- Очищаем временные таблицы
TRUNCATE TABLE #tt13
Запрос стал больше и сложнее, но на самом деле ничего сверхъестественного тут нет:
- Сначала платформа подготавливает информацию по счету, который используется в отборах и по виду субконто.
- Затем начинается основной запрос, в котором:
- Получаем данные оборотов из таблицы итогов по 2 субконто. Почему именно по этой таблице? Да потому что во всех отборах нашего примера устанавливался счет 51, у которого 2 субконто (банковский счет и статья ДДС). Для получаемых значений также устанавливается фильтр по счету и субконто, а также по организации. Отбор по периоду устанавливается в промежутке от 01.01.2019 (начала периода рассчитанных итогов) до 28.02.2020 включительно.
- Т.к. изначально отбор ставился с 10.12.2018, то платформа делает дополнительных два запроса (по дебету и кредиту) к основным таблицам регистра в период с 10.12.2018 по 31.12.2018, т.к. получить обороты из таблиц итогов за этот период нельзя.
- Дополнительно делаются аналогичные два запроса (по дебету и по кредиту) за 01.03.2020, т.к. именно эта дата установлена как конец периода отбора. Т.к. итоги рассчитываются в разрезе месяца, то получить данные оборотов за один день с их помощью не получится. Именно поэтому и был еще раз сгенерирован запрос к основным таблицам регистра.
- После все полученные данные группируются и возвращаются как результат.
В самом запросе дал исчерпывающие комментарии, внимательно посмотрите на него, чтобы лучше разобраться.
Данные кор. счета
Немного изменим пример, добавив кор. счет с аналитикой и обороты по количеству (т.к. это не балансовый ресурс) по нему.
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК Субконто1,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоОборот,
ХозрасчетныйОбороты.КоличествоОборотДт КАК КоличествоОборотДт,
ХозрасчетныйОбороты.КоличествоОборотКт КАК КоличествоОборотКт,
ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот,
ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаОборотДт,
ХозрасчетныйОбороты.СуммаОборотКт КАК СуммаОборотКт,
ХозрасчетныйОбороты.КорСчет КАК КорСчет,
ХозрасчетныйОбороты.КоличествоКорОборот КАК КоличествоКорОборот,
ХозрасчетныйОбороты.КоличествоКорОборотДт КАК КоличествоКорОборотДт,
ХозрасчетныйОбороты.КоличествоКорОборотКт КАК КоличествоКорОборотКт
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачалоПериода,
&КонецПериода,
,
Счет = &Счет,
&ВидСубконто,
Организация = &Организация
И Субконто1 = &Субконто1,
,
) КАК ХозрасчетныйОбороты
Сгенерированный платформой 1С запрос очень похож на тот, что было в предыдущем примере, но логика получения данных значительно изменилась.
SELECT
T1.AccountRRef,
T1.Value1_TYPE,
T1.Value1_RTRef,
T1.Value1_RRRef,
T1.Fld792Turnover_,
T1.Fld792TurnoverDt_,
T1.Fld792TurnoverCt_,
T1.Fld790Turnover_,
T1.Fld790TurnoverDt_,
T1.Fld790TurnoverCt_,
T1.BalancedAccountRRef,
T1.Fld792BalTurnover_,
T1.Fld792BalTurnoverDt_,
T1.Fld792BalTurnoverCt_
FROM (SELECT
T2.AccountRRef AS AccountRRef,
T2.Value1_TYPE AS Value1_TYPE,
T2.Value1_RTRef AS Value1_RTRef,
T2.Value1_RRRef AS Value1_RRRef,
T2.BalancedAccountRRef AS BalancedAccountRRef,
CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) END AS Fld790Turnover_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) END AS Fld790TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) END AS Fld790TurnoverCt_,
CASE WHEN CAST(SUM(T2.Fld790BalTurnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790BalTurnover_) AS NUMERIC(21, 2)) END AS Fld790BalTurnover_,
CASE WHEN CAST(SUM(T2.Fld790BalTurnoverDt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790BalTurnoverDt_) AS NUMERIC(21, 2)) END AS Fld790BalTurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld790BalTurnoverCt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790BalTurnoverCt_) AS NUMERIC(21, 2)) END AS Fld790BalTurnoverCt_,
CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) END AS Fld792Turnover_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) END AS Fld792TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) END AS Fld792TurnoverCt_,
CASE WHEN CAST(SUM(T2.Fld792BalTurnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792BalTurnover_) AS NUMERIC(21, 3)) END AS Fld792BalTurnover_,
CASE WHEN CAST(SUM(T2.Fld792BalTurnoverDt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792BalTurnoverDt_) AS NUMERIC(21, 3)) END AS Fld792BalTurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld792BalTurnoverCt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792BalTurnoverCt_) AS NUMERIC(21, 3)) END AS Fld792BalTurnoverCt_
FROM (
SELECT
T3._AccountDtRRef AS AccountRRef,
T4._Value_TYPE AS Value1_TYPE,
T4._Value_RTRef AS Value1_RTRef,
T4._Value_RRRef AS Value1_RRRef,
T3._AccountCtRRef AS BalancedAccountRRef,
T3._Fld790 AS Fld790Turnover_,
T3._Fld790 AS Fld790TurnoverDt_,
CAST(@P1 AS NUMERIC(15, 2)) AS Fld790TurnoverCt_,
T3._Fld790 AS Fld790BalTurnover_,
T3._Fld790 AS Fld790BalTurnoverDt_,
CAST(@P2 AS NUMERIC(15, 2)) AS Fld790BalTurnoverCt_,
T3._Fld792Dt AS Fld792Turnover_,
T3._Fld792Dt AS Fld792TurnoverDt_,
CAST(CASE WHEN T3._Fld792Dt IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P3 END AS NUMERIC(15, 3)) AS Fld792TurnoverCt_,
T3._Fld792Ct AS Fld792BalTurnover_,
T3._Fld792Ct AS Fld792BalTurnoverDt_,
CAST(CASE WHEN T3._Fld792Ct IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P4 END AS NUMERIC(15, 3)) AS Fld792BalTurnoverCt_
FROM dbo._AccRg786 T3 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T4 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T4._RecorderTRef = T3._RecorderTRef AND T4._RecorderRRef = T3._RecorderRRef AND T4._LineNo = T3._LineNo AND T4._Period = T3._Period AND T4._Correspond = @P5 AND T4._KindRRef = @P6 AND T4._Period >= @P7 AND T4._Period <= @P8
WHERE (((T3._Fld774 = @P9)) AND (T4._Fld774 = @P10)) AND (T3._Active = 0x01 AND T3._AccountDtRRef <> @P11 AND ((T3._AccountDtRRef = @P12)) AND ((T3._Fld787RRef = @P13) AND (T4._Value_TYPE = 0x08 AND T4._Value_RTRef = 0x0000001A AND T4._Value_RRRef = @P14)) AND T3._Period >= @P15 AND T3._Period <= @P16)
UNION ALL
SELECT
T5._AccountCtRRef AS AccountRRef,
T6._Value_TYPE AS Value1_TYPE,
T6._Value_RTRef AS Value1_RTRef,
T6._Value_RRRef AS Value1_RRRef,
T5._AccountDtRRef AS BalancedAccountRRef,
-T5._Fld790 AS Fld790Turnover_,
CAST(@P17 AS NUMERIC(15, 2)) AS Fld790TurnoverDt_,
T5._Fld790 AS Fld790TurnoverCt_,
-T5._Fld790 AS Fld790BalTurnover_,
CAST(@P18 AS NUMERIC(15, 2)) AS Fld790BalTurnoverDt_,
T5._Fld790 AS Fld790BalTurnoverCt_,
-T5._Fld792Ct AS Fld792Turnover_,
CAST(CASE WHEN T5._Fld792Ct IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P19 END AS NUMERIC(15, 3)) AS Fld792TurnoverDt_,
T5._Fld792Ct AS Fld792TurnoverCt_,
-T5._Fld792Dt AS Fld792BalTurnover_,
CAST(CASE WHEN T5._Fld792Dt IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P20 END AS NUMERIC(15, 3)) AS Fld792BalTurnoverDt_,
T5._Fld792Dt AS Fld792BalTurnoverCt_
FROM dbo._AccRg786 T5 -- РегистрБухгалтерии.Хозрасчетный.Основная
INNER JOIN dbo._AccRgED823 T6 -- РегистрБухгалтерии.Хозрасчетный.Субконто
ON T6._RecorderTRef = T5._RecorderTRef AND T6._RecorderRRef = T5._RecorderRRef AND T6._LineNo = T5._LineNo AND T6._Period = T5._Period AND T6._Correspond = @P21 AND T6._KindRRef = @P22 AND T6._Period >= @P23 AND T6._Period <= @P24
WHERE (((T5._Fld774 = @P25)) AND (T6._Fld774 = @P26)) AND (T5._Active = 0x01 AND T5._AccountCtRRef <> @P27 AND ((T5._AccountCtRRef = @P28)) AND ((T5._Fld787RRef = @P29) AND (T6._Value_TYPE = 0x08 AND T6._Value_RTRef = 0x0000001A AND T6._Value_RRRef = @P30)) AND T5._Period >= @P31 AND T5._Period <= @P32)) T2
GROUP BY T2.AccountRRef,
T2.Value1_TYPE,
T2.Value1_RTRef,
T2.Value1_RRRef,
T2.BalancedAccountRRef
Т.к. у нас присутствует получение данных по корреспондентскому счету, то итоги в этом случае использовать уже невозможно. В таблице итогов просто нет заранее подготовленных данных для подобных запросов. Поэтому вся информация по оборотом "вытягивается" из основных таблиц регистров.
Будьте осторожны с получением данных оборотов по корреспондирующим счетам и их аналитике.
Особая периодичность
В качестве последнего примера рассмотрим тот же запрос, но с небольшой модификацией - установим параметр виртуальной таблицы "Периодичность" в "Неделя" и посмотрим что будет.
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК Субконто1,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоОборот,
ХозрасчетныйОбороты.КоличествоОборотДт КАК КоличествоОборотДт,
ХозрасчетныйОбороты.КоличествоОборотКт КАК КоличествоОборотКт,
ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот,
ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаОборотДт,
ХозрасчетныйОбороты.СуммаОборотКт КАК СуммаОборотКт
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачалоПериода,
&КонецПериода,
Неделя,
Счет = &Счет,
&ВидСубконто,
Организация = &Организация
И Субконто1 = &Субконто1,
,
) КАК ХозрасчетныйОбороты
Сформированный SQL-запрос, который в этот раз я оставлю без комментариев, показывает, что и в этом случае таблицы итогов не были задействованы. Подобное поведение характерно и для регистра накопления, которое мы рассматривали в предыдущей статье. Заключается оно в том, что если периодичность установлена меньше месяца, то итоги никаким образом не помогут в получении данных, ведь они рассчитаны в разрезе месяца.
В примере выше уже была ситуация, когда для получения данных за один день платформа формировала SQL-запросы к основным таблицам регистра. Тут схожая ситуация.
SELECT
T1.AccountRRef,
T1.Value1_TYPE,
T1.Value1_RTRef,
T1.Value1_RRRef,
T1.Fld792Turnover_,
T1.Fld792TurnoverDt_,
T1.Fld792TurnoverCt_,
T1.Fld790Turnover_,
T1.Fld790TurnoverDt_,
T1.Fld790TurnoverCt_
FROM (SELECT
T2.Period_ AS Period_,
T2.AccountRRef AS AccountRRef,
T2.Value1_TYPE AS Value1_TYPE,
T2.Value1_RTRef AS Value1_RTRef,
T2.Value1_RRRef AS Value1_RRRef,
CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) END AS Fld790Turnover_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) END AS Fld790TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) END AS Fld790TurnoverCt_,
CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) END AS Fld792Turnover_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) END AS Fld792TurnoverDt_,
CASE WHEN CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) END AS Fld792TurnoverCt_
FROM (
SELECT
DATEADD(DAY,CAST(DATEPART(DAYOFYEAR,T3._Period) AS NUMERIC(4)) - CAST(DATEPART(WEEKDAY,T3._Period) AS NUMERIC(4, 0)),DATETIME2FROMPARTS(DATEPART(YEAR,T3._Period),1,1,0,0,0,0,0)) AS Period_,
T3._AccountDtRRef AS AccountRRef,
T4._Value_TYPE AS Value1_TYPE,
T4._Value_RTRef AS Value1_RTRef,
T4._Value_RRRef AS Value1_RRRef,
T3._Fld790 AS Fld790Turnover_,
T3._Fld790 AS Fld790TurnoverDt_,
CAST(@P1 AS NUMERIC(15, 2)) AS Fld790TurnoverCt_,
T3._Fld792Dt AS Fld792Turnover_,
T3._Fld792Dt AS Fld792TurnoverDt_,
CAST(CASE WHEN T3._Fld792Dt IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P2 END AS NUMERIC(15, 3)) AS Fld792TurnoverCt_
FROM dbo._AccRg786 T3
INNER JOIN dbo._AccRgED823 T4
ON T4._RecorderTRef = T3._RecorderTRef AND T4._RecorderRRef = T3._RecorderRRef AND T4._LineNo = T3._LineNo AND T4._Period = T3._Period AND T4._Correspond = @P3 AND T4._KindRRef = @P4 AND T4._Period >= @P5 AND T4._Period <= @P6
WHERE (((T3._Fld774 = @P7)) AND (T4._Fld774 = @P8)) AND (T3._Active = 0x01 AND T3._AccountDtRRef <> @P9 AND ((T3._AccountDtRRef = @P10)) AND ((T3._Fld787RRef = @P11) AND (T4._Value_TYPE = 0x08 AND T4._Value_RTRef = 0x0000001A AND T4._Value_RRRef = @P12)) AND T3._Period >= @P13 AND T3._Period <= @P14)
UNION ALL
SELECT
DATEADD(DAY,CAST(DATEPART(DAYOFYEAR,T5._Period) AS NUMERIC(4)) - CAST(DATEPART(WEEKDAY,T5._Period) AS NUMERIC(4, 0)),DATETIME2FROMPARTS(DATEPART(YEAR,T5._Period),1,1,0,0,0,0,0)) AS Period_,
T5._AccountCtRRef AS AccountRRef,
T6._Value_TYPE AS Value1_TYPE,
T6._Value_RTRef AS Value1_RTRef,
T6._Value_RRRef AS Value1_RRRef,
-T5._Fld790 AS Fld790Turnover_,
CAST(@P15 AS NUMERIC(15, 2)) AS Fld790TurnoverDt_,
T5._Fld790 AS Fld790TurnoverCt_,
-T5._Fld792Ct AS Fld792Turnover_,
CAST(CASE WHEN T5._Fld792Ct IS NULL THEN CAST(NULL AS NUMERIC(38,8)) ELSE @P16 END AS NUMERIC(15, 3)) AS Fld792TurnoverDt_,
T5._Fld792Ct AS Fld792TurnoverCt_
FROM dbo._AccRg786 T5
INNER JOIN dbo._AccRgED823 T6
ON T6._RecorderTRef = T5._RecorderTRef AND T6._RecorderRRef = T5._RecorderRRef AND T6._LineNo = T5._LineNo AND T6._Period = T5._Period AND T6._Correspond = @P17 AND T6._KindRRef = @P18 AND T6._Period >= @P19 AND T6._Period <= @P20
WHERE (((T5._Fld774 = @P21)) AND (T6._Fld774 = @P22)) AND (T5._Active = 0x01 AND T5._AccountCtRRef <> @P23 AND ((T5._AccountCtRRef = @P24)) AND ((T5._Fld787RRef = @P25) AND (T6._Value_TYPE = 0x08 AND T6._Value_RTRef = 0x0000001A AND T6._Value_RRRef = @P26)) AND T5._Period >= @P27 AND T5._Period <= @P28)) T2
GROUP BY T2.Period_,
T2.AccountRRef,
T2.Value1_TYPE,
T2.Value1_RTRef,
T2.Value1_RRRef
HAVING (CASE WHEN CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790Turnover_) AS NUMERIC(21, 2)) END) <> 0.0 OR (CASE WHEN CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverDt_) AS NUMERIC(21, 2)) END) <> 0.0 OR (CASE WHEN CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld790TurnoverCt_) AS NUMERIC(21, 2)) END) <> 0.0 OR (CASE WHEN CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792Turnover_) AS NUMERIC(21, 3)) END) <> 0.0 OR (CASE WHEN CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverDt_) AS NUMERIC(21, 3)) END) <> 0.0 OR (CASE WHEN CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) IS NULL THEN 0.0 ELSE CAST(SUM(T2.Fld792TurnoverCt_) AS NUMERIC(21, 3)) END) <> 0.0) T1
Использовать периодичность нужно очень осторожно, т.к. некорректное ее указание может значительно снизить производительность информационной системы.
Производительность запросов
Мы рассмотрели несколько примеров работы виртуальной таблицы "Обороты" регистра бухгалтерии. В контексте производительности напрашиваются такие выводы:
- Для максимальной производительности запросов к виртуальной таблице оборотов необходимо использовать итоги регистра. То есть они должны быть рассчитаны и обслуживаться ежемесячно, сдвигая дату рассчитанных итогов. Об этом мы говорили ранее.
- Даже если итоги в базе рассчитаны и обслуживаются, необходимо придерживаться некоторых правил написания запросов к таблице оборотов, когда это возможно в контексте поставленной задачи:
- Периодичность не должна быть меньше месяца, иначе данные будут запрашиваться из основных таблиц регистра и итоги будут игнорироваться.
- Обязательно нужно указывать период, за который выполняется получение данных оборотов, иначе все данные будут получаться из основных таблиц регистра. Да, если не указать период в таблице оборотов - итоги не будут использоваться. Но Вы же так не делаете, верно?
- Использование корреспондирующих полей (кор. счет или измерения, а также ресурсы) также приводит к отключению использования итогов. Будьте осторожны.
- При работе с оборотами продумайте максимально возможные условия отборов, как минимум по периоду и основным измерениям регистра, счетам и субконто (виду и значениям). Это позволит избежать большинства проблем производительности при использовании этой виртуальной таблицы.
- Для максимальной производительности отборы должны быть максимально селективными. Если Вы получаете обороты за пару лет с указанием отбора только по счету, то потенциальный объем получаемых данных может быть очень большой. В этом случае СУБД может проигнорировать индексы, т.к. ей проще эти данные получить сканированием таблиц.
Таким образом, использовать виртуальную таблицу оборотов регистра бухгалтерии можно и нужно, но стоит учитывать ее особенности работы.
В случае же, когда производительность при получения данных оборотов критична, то необходимо рассмотреть вариант создания отдельного регистра накопления, как это часто можно увидеть в типовых конфигурациях. Регистры накопления работают значительно быстрее с оборотами, т.к. выполняют меньше функций, а структура регистра накопления значительно проще. Да и агрегаты этих регистров, если их использовать с умом, позволяют решать проблемы производительности при получении оборотов практически любого уровня.
Следующий раунд
Среди всех виртуальных таблиц регистров бухгалтерии именно таблица "Обороты" является самой "легкой" как в использовании в запросах, так и с точки зрения потребления ресурсов и производительности. Однако, даже она по производительности часто уступает таблице оборотов регистра накопления и это нормально. Ведь у этих регистров совершенно разные задачи.
Регистры бухгалтерии достаточно сложные и "тяжелые" как с точки зрения хранения данных, так и в части использования в разработке. Именно поэтому нужно использовать их для учетных задач, но никак не для оперативного учета. Представьте что было бы, если бы чеки ККМ формировали проводки при проведении :) Прощай розница! :)))
Все данные для публикации получены с помощью инструментов:
В следующих статьях мы подробнее рассмотрим остальные виртуальные таблицы регистров, а после поднимем тему неплатформенных индексов для них, влияния количества субконто на производительность, а также несколько конкретных кейсов запросов к регистру и их оптимизацию.
До скорых встреч!
Другие ссылки
Авторские разработки