Сразу скажу, что ни в коей мере не претендую на авторство и оригинальность - почти уверен что все это уже давно есть на просторах интернета, но когда решение такой задачи понадобилось мне самому - сходу ответа не нашел, скомпилировал из кусков решение, и предоставляю его тут - может кому и пригодится. Так же не претендую на быстродействие - меня оное устроило в моей задаче, однако думаю можно поработать над оптимизацией. Запрос писался на самопальной базе, поэтому имена регистров и его измерений отличается от типовых, но думаю переименовать по тексту запроса труда не составит. Готовый запрос с выводом - в обработке, засунутой в качестве вложения к этой статейке.
Итак - ситуация которая может встретиться любому разработчику. Требуется отчет показывающий остатки товаров (денег, взаиморасчетов - чего угодно) на каждый день (или какой еще отрезок времени) в течении заданного периода отчета. Задача в общем-то несложная, но из области тех, которые не решаются просто получением данных из какой-либо таблицы регистра.
Виртуальная таблица регистра накопления, которая может хоть как-то дать данные по остаткам в периоде и попериодно - это таблица "Остатки и обороты" (прошу простить профи за ликбез - но вдруг попадется начинающий читатель). Однако и она не подходит для нашей задачи в чистом виде. Дело в том, что виртуальная таблица остатков и оборотов за заданный период выдаст только записи с данными по остаткам на начало заданного периода, на конец заданного периода, и на те даты, в которые были движения в этом периоде. То есть для примера со скрина, результат получения данных (остатка на конец) из виртуальной таблицы "Остатки и обороты" за май 2016-го с периодичностью в день, будет следующим:
Период | Номенклатура | Количество |
01.05.2016 | Товар 01 | 4 |
11.05.2016 | Товар 01 | 3 |
16.05.2016 | Товар 01 | 2 |
31.05.2016 | Товар 01 | 2 |
Нам же в рамках нашей задачи нужна таблица, показывающая остаток на каждый день месяца:
Период | Номенклатура | Количество |
01.05.2016 | Товар 01 | 4 |
02.05.2016 | Товар 01 | 4 |
03.05.2016 | Товар 01 | 4 |
04.05.2016 | Товар 01 | 4 |
05.05.2016 | Товар 01 | 4 |
06.05.2016 | Товар 01 | 4 |
07.05.2016 | Товар 01 | 4 |
08.05.2016 | Товар 01 | 4 |
09.05.2016 | Товар 01 | 4 |
10.05.2016 | Товар 01 | 4 |
11.05.2016 | Товар 01 | 3 |
12.05.2016 | Товар 01 | 3 |
13.05.2016 | Товар 01 | 3 |
14.05.2016 | Товар 01 | 3 |
15.05.2016 | Товар 01 | 3 |
16.05.2016 | Товар 01 | 2 |
17.05.2016 | Товар 01 | 2 |
18.05.2016 | Товар 01 | 2 |
19.05.2016 | Товар 01 | 2 |
20.05.2016 | Товар 01 | 2 |
21.05.2016 | Товар 01 | 2 |
22.05.2016 | Товар 01 | 2 |
23.05.2016 | Товар 01 | 2 |
24.05.2016 | Товар 01 | 2 |
25.05.2016 | Товар 01 | 2 |
26.05.2016 | Товар 01 | 2 |
27.05.2016 | Товар 01 | 2 |
28.05.2016 | Товар 01 | 2 |
29.05.2016 | Товар 01 | 2 |
30.05.2016 | Товар 01 | 2 |
31.05.2016 | Товар 01 | 2 |
Посмотрим как мы можем это получить. Вот текст самого запроса:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d) КАК Период
ПОМЕСТИТЬ ВТ_ТабДат
ИЗ
(ВЫБРАТЬ
0 КАК a
ОБЪЕДИНИТЬ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ
ВЫБРАТЬ
9) КАК aa
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
0 КАК b
ОБЪЕДИНИТЬ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ
ВЫБРАТЬ
9) КАК bb
ПО (ИСТИНА)
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
0 КАК c
ОБЪЕДИНИТЬ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ
ВЫБРАТЬ
9) КАК cc
ПО (ИСТИНА)
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
0 КАК d
ОБЪЕДИНИТЬ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ
ВЫБРАТЬ
9) КАК dd
ПО (ИСТИНА)
ГДЕ
aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d <= РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, ДЕНЬ)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_ТабТоваров
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.ЭтоГруппа
И НЕ Номенклатура.ПометкаУдаления
И Номенклатура.ВидНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ВидыНоменклатуры.Товар)
И Номенклатура.Ссылка = &ПарамОтборТовар
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ТабДат.Период,
ВТ_ТабТоваров.Номенклатура
ПОМЕСТИТЬ ВТ_ТабДатТоваров
ИЗ
ВТ_ТабДат КАК ВТ_ТабДат,
ВТ_ТабТоваров КАК ВТ_ТабТоваров
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ТабДатТоваров.Номенклатура,
ВТ_ТабДатТоваров.Период,
МАКСИМУМ(ОстаткиТоваровОстаткиИОбороты.Период) КАК ПериодРегистра
ПОМЕСТИТЬ ВТ_ТабМаксимальныхПериодов
ИЗ
ВТ_ТабДатТоваров КАК ВТ_ТабДатТоваров
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ОстаткиТоваровОстаткиИОбороты
ПО ВТ_ТабДатТоваров.Номенклатура = ОстаткиТоваровОстаткиИОбороты.Номенклатура
И ВТ_ТабДатТоваров.Период >= ОстаткиТоваровОстаткиИОбороты.Период
СГРУППИРОВАТЬ ПО
ВТ_ТабДатТоваров.Номенклатура,
ВТ_ТабДатТоваров.Период
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ТабМаксимальныхПериодов.Номенклатура,
ВТ_ТабМаксимальныхПериодов.Период КАК Период,
ОстаткиТоваровОстаткиИОбороты.КоличествоКонечныйОстаток КАК Остатки
ИЗ
ВТ_ТабМаксимальныхПериодов КАК ВТ_ТабМаксимальныхПериодов
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ОстаткиТоваровОстаткиИОбороты
ПО ВТ_ТабМаксимальныхПериодов.ПериодРегистра = ОстаткиТоваровОстаткиИОбороты.Период
И ВТ_ТабМаксимальныхПериодов.Номенклатура = ОстаткиТоваровОстаткиИОбороты.Номенклатура
УПОРЯДОЧИТЬ ПО
Период
Запрос идет по временным таблицам - как бы даже не исбыточно, но так читается удобнее. По таблицам:
1. Получаем таблицу с дневными периодами - только даты начала каждого дня (это точно не мое!);
2. Получаем таблицу номенклатуры из справочника (чтобы выводить в результат даже ту номенклатуру, по которой записей в регистре отродясь не было);
3. Соединяем эти две таблицы полным соединением, без указания связи (для педантов: Можно добавить строчку "ПО ИСТИНА" - рещультат будет тот же);
4. Соединяем левым соединением таблицу периодов с номенклатурой с виртуальной таблицей регистра "Остатки и обороты". Периодичность таблицы - день, метод дополнения не указываем. Период выборки задаем тот же, что и для таблицы периодов. Тут основная хитрость в связи. Связываем таблицы по Номенклатуре и Периоду, но период сравниваем не на равенство, а на ПериодТаблицыПериодов >= ПериодТаблицыОстатковИОборотовРегистра
Таким образом например для даты 15.05.2016 мы получим набор строк - все попадающие по условию связи:
Номенклатура | Период (таблица периодов) | Период (таблица регистра) |
Товар 01 | 15.05.2016 | 01.05.2016 |
Товар 01 | 15.05.2016 | 11.05.2016 |
Сами цифирки в этой таблице мы не вытаскиваем - они нам пока не нужны. А эту табличку мы группируем по периоду из таблицы периодов и номенклатуре. Период таблицы регистра получаем максимальный "МАКСИМУМ(ОстаткиТоваровОстаткиИОбороты.Период) КАК ПериодРегистра". Таким образом наша табличка примера схлопывается до одной строки:
Номенклатура | Период (таблица периодов) | Период (таблица регистра) |
Товар 01 | 15.05.2016 | 11.05.2016 |
...и мы получаем полезную таблицу, в которой есть номенклатура, период конкретного дня отчета, и дата максимального периода из таблицы регистра, по которому можно вытащить актуальную для этого дня цифирку.
5. Собственно и получаем актуальные остатки - связав с нашей таблицей максимальных периодов, таблицу остатков и оборотов по регистру (используем ту же виртуальную таблицу второй раз). Связь так же по номенклатуре и по колонке максимальных дат.
Вот такое вот решение задачи. Меня оно устроило - может еще кому пригодится.