Эта версия благодаря поддержке сообщества переделана, так сказать без единого гвоздя: один набор данных запрос и один параметр СтандартныйПериод, модуля у отчета нет, на выходе готовый календарь.
Напомню, что предыдущая версия имеет модуль с процедурой ПриКомпоновкеРезультата и остаётся полезной как пример программной работы с компоновкой – источник данных внешний набор данных Результат запроса, а также как пример работы с запросом - источник данных для запроса Таблица значений, переданная параметром.
Набор данных запрос этого отчета полностью самостоятельный, а отдельные его части могут быть полезны в решении различных нестандартных задач.
Корректно работает только период целыми кварталами!
Итак описание:
// Объединяем все числа русского языка:)
ВЫБРАТЬ
0 КАК Поле1
ПОМЕСТИТЬ ВТ_Числа
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
9
;
// Далее объединяя ВТ_Числа четыре раза имеем все возможные комбинации от 0,0,0,0 до 9,9,9,9
// Умножая цифру на разряд и складывая получаем линейку от 0 до 9999 с шагом 1
// Этого хватит на 27 лет подряд
ВЫБРАТЬ
ВТ_Числа1.Поле1 + ВТ_Числа10.Поле1 * 10 + ВТ_Числа100.Поле1 * 100 + ВТ_Числа1000.Поле1 * 1000 КАК Поле1
ПОМЕСТИТЬ ВТ_Линейка
ИЗ
ВТ_Числа КАК ВТ_Числа1,
ВТ_Числа КАК ВТ_Числа10,
ВТ_Числа КАК ВТ_Числа100,
ВТ_Числа КАК ВТ_Числа1000
;
// Прибавляем к дате начала отчета все числа дней и отбираем при этом подходящие нам
// то есть не больше даты конца отчета, получим все даты периода формирования
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&НачПериода, ДЕНЬ, ВТ_Линейка.Поле1) КАК Дата
ПОМЕСТИТЬ ВТ_ВсеДаты
ИЗ
ВТ_Линейка КАК ВТ_Линейка
ГДЕ
ДОБАВИТЬКДАТЕ(&НачПериода, ДЕНЬ, ВТ_Линейка.Поле1) <= &КонПериода
;
// Теперь разложим даты по дням недели, для этого к каждому воскресенью присоединим
// по очереди день –1 (суббота), день –2 (пятница) и т.д.
// Здесь же появляется «Синус Хэ» Индекс - это остаток от деления номера месяца на 3,
// он принимает значения -0,333; 0; +0,333 и пригодится для склеивания месяцев в календарь
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Даты.Дата, МЕСЯЦ) КАК Мес,
Даты6.Дата КАК Пн,
Даты5.Дата КАК Вт,
Даты4.Дата КАК Ср,
Даты3.Дата КАК Чт,
Даты2.Дата КАК Пт,
Даты1.Дата КАК Сб,
Даты.Дата КАК Вс,
(МЕСЯЦ(Даты.Дата) - 1) / 3 - (ВЫРАЗИТЬ((МЕСЯЦ(Даты.Дата) - 1) / 3 КАК ЧИСЛО(10, 0))) КАК Индекс
ПОМЕСТИТЬ ВТ_Календарь
ИЗ
ВТ_ВсеДаты КАК Даты
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты1
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты1.Дата, ДЕНЬ) = -1)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты2
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты2.Дата, ДЕНЬ) = -2)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты3
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты3.Дата, ДЕНЬ) = -3)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты4
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты4.Дата, ДЕНЬ) = -4)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты5
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты5.Дата, ДЕНЬ) = -5)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты6
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты6.Дата, ДЕНЬ) = -6)
ГДЕ
ДЕНЬНЕДЕЛИ(Даты.Дата) = 7
ОБЪЕДИНИТЬ ВСЕ
// Второй запрос нужен чтобы, во-первых добавить последнюю неделю периода для которой воскресенья может не оказаться,
// во-вторых чтобы добавить недели с переходными месяцами, то есть если на неделе №6 январь заканчивается,
// а февраль начинается, то нужна такая строчка для января и такая же для февраля ***
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Пн, МЕСЯЦ),
ВложенныйЗапрос.Пн,
ВложенныйЗапрос.Вт,
ВложенныйЗапрос.Ср,
ВложенныйЗапрос.Чт,
ВложенныйЗапрос.Пт,
ВложенныйЗапрос.Сб,
ВложенныйЗапрос.Вс,
(МЕСЯЦ(ВложенныйЗапрос.Пн) - 1) / 3 - (ВЫРАЗИТЬ((МЕСЯЦ(ВложенныйЗапрос.Пн) - 1) / 3 КАК ЧИСЛО(10, 0)))
ИЗ
(ВЫБРАТЬ
Даты.Дата КАК Пн,
Даты1.Дата КАК Вт,
Даты2.Дата КАК Ср,
Даты3.Дата КАК Чт,
Даты4.Дата КАК Пт,
Даты5.Дата КАК Сб,
Даты6.Дата КАК Вс,
ЕСТЬNULL(Даты6.Дата, ИСТИНА) КАК Вс0
ИЗ
ВТ_ВсеДаты КАК Даты
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты1
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты1.Дата, ДЕНЬ) = 1)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты2
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты2.Дата, ДЕНЬ) = 2)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты3
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты3.Дата, ДЕНЬ) = 3)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты4
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты4.Дата, ДЕНЬ) = 4)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты5
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты5.Дата, ДЕНЬ) = 5)
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДаты КАК Даты6
ПО (РАЗНОСТЬДАТ(Даты.Дата, Даты6.Дата, ДЕНЬ) = 6)
ГДЕ
ДЕНЬНЕДЕЛИ(Даты.Дата) = 1) КАК ВложенныйЗапрос
ГДЕ
(НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Пн, МЕСЯЦ) <> НАЧАЛОПЕРИОДА(ВложенныйЗапрос.Вс, МЕСЯЦ)
ИЛИ ВложенныйЗапрос.Вс0 = ИСТИНА)
;
// *** Теперь выбираем дни которые в своих месяцах, то есть если 6-я неделя января то даты от февраля берём = NULL,
// а эта же неделя для февраля - даты января берём за NULL и переводим даты в числа месяца, месяц и год от даты
// нам больше не понадобятся. Уже после этого шага получим календарь в один столбик
ВЫБРАТЬ
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Вс, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА НЕДЕЛЯ(ВТ_Календарь.Вс)
ИНАЧЕ НЕДЕЛЯ(ВТ_Календарь.Пн)
КОНЕЦ КАК Нед,
ВТ_Календарь.Мес КАК Мес,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Пн, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Пн)
ИНАЧЕ NULL
КОНЕЦ КАК Пн,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Вт, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Вт)
ИНАЧЕ NULL
КОНЕЦ КАК Вт,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Ср, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Ср)
ИНАЧЕ NULL
КОНЕЦ КАК Ср,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Чт, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Чт)
ИНАЧЕ NULL
КОНЕЦ КАК Чт,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Пт, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Пт)
ИНАЧЕ NULL
КОНЕЦ КАК Пт,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Сб, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Сб)
ИНАЧЕ NULL
КОНЕЦ КАК Сб,
ВЫБОР
КОГДА НАЧАЛОПЕРИОДА(ВТ_Календарь.Вс, МЕСЯЦ) = ВТ_Календарь.Мес
ТОГДА ДЕНЬ(ВТ_Календарь.Вс)
ИНАЧЕ NULL
КОНЕЦ КАК Вс,
ВТ_Календарь.Индекс
ПОМЕСТИТЬ ВТ_Индекс
ИЗ
ВТ_Календарь КАК ВТ_Календарь
;
// Осталось скрутить месяцы по три, для этого каждой неделе с Индекс > 0 (это месяца янв, апр, июл и т.д.)
// присоединим неделю у которой номер по порядку в месяце такой же, а сам месяц на один больше,
// второе соединение - месяц на два больше
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВТ_Индекс.Мес, ГОД) КАК Г,
ВТ_Индекс.Мес КАК Мес,
ВТ_Индекс.Нед КАК Нед,
ВТ_Индекс.Пн КАК Пн,
ВТ_Индекс.Вт,
ВТ_Индекс.Ср,
ВТ_Индекс.Чт,
ВТ_Индекс.Пт,
ВТ_Индекс.Сб,
ВТ_Индекс.Вс,
ВТ_Индекс1.Мес КАК Мес1,
ВТ_Индекс1.Нед КАК Нед1,
ВТ_Индекс1.Пн КАК Пн1,
ВТ_Индекс1.Вт КАК Вт1,
ВТ_Индекс1.Ср КАК Ср1,
ВТ_Индекс1.Чт КАК Чт1,
ВТ_Индекс1.Пт КАК Пт1,
ВТ_Индекс1.Сб КАК Сб1,
ВТ_Индекс1.Вс КАК Вс1,
ВТ_Индекс2.Мес КАК Мес2,
ВТ_Индекс2.Нед КАК Нед2,
ВТ_Индекс2.Пн КАК Пн2,
ВТ_Индекс2.Вт КАК Вт2,
ВТ_Индекс2.Ср КАК Ср2,
ВТ_Индекс2.Чт КАК Чт2,
ВТ_Индекс2.Пт КАК Пт2,
ВТ_Индекс2.Сб КАК Сб2,
ВТ_Индекс2.Вс КАК Вс2
ИЗ
ВТ_Индекс КАК ВТ_Индекс
ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Индекс КАК ВТ_Индекс1
ПО (РАЗНОСТЬДАТ(ВТ_Индекс.Мес, ВТ_Индекс1.Мес, МЕСЯЦ) = 1)
И (ВТ_Индекс.Нед - НЕДЕЛЯ(ВТ_Индекс.Мес) = ВТ_Индекс1.Нед - НЕДЕЛЯ(ВТ_Индекс1.Мес))
ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Индекс КАК ВТ_Индекс2
ПО (ВТ_Индекс.Нед - НЕДЕЛЯ(ВТ_Индекс.Мес) = ВТ_Индекс2.Нед - НЕДЕЛЯ(ВТ_Индекс2.Мес))
И (РАЗНОСТЬДАТ(ВТ_Индекс.Мес, ВТ_Индекс2.Мес, МЕСЯЦ) = 2)
ГДЕ
ВТ_Индекс.Индекс = 0
ОБЪЕДИНИТЬ ВСЕ
// Здесь добавляем недели из вторых месяцев с номером по порядку в месяце большем чем
// максимально возможный в первых, так как они вылетают из предыдущей выборки
// Это например 6 неделя мая 2011 (30,31), потому как в апреле 2011 5 недель
// Первые три поля выборки при этом заполняются для дальнейшей сортировки
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВТ_Индекс.Мес, ГОД),
ВТ_Индекс.Мес,
ВТ_Индекс.Нед,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ВТ_Индекс.Мес,
ВТ_Индекс.Нед,
ВТ_Индекс.Пн,
ВТ_Индекс.Вт,
ВТ_Индекс.Ср,
ВТ_Индекс.Чт,
ВТ_Индекс.Пт,
ВТ_Индекс.Сб,
ВТ_Индекс.Вс,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
ИЗ
ВТ_Индекс КАК ВТ_Индекс
ГДЕ
ВТ_Индекс.Индекс > 0
И ВТ_Индекс.Нед - НЕДЕЛЯ(ВТ_Индекс.Мес) - (НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(ВТ_Индекс.Мес, ДЕНЬ, -1)) - НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(ВТ_Индекс.Мес, МЕСЯЦ, -1))) > 0
ОБЪЕДИНИТЬ ВСЕ
// А это 31 декабря 2012, ну по той же схеме только для третьего ряда месяцев
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВТ_Индекс.Мес, ГОД),
ВТ_Индекс.Мес,
ВТ_Индекс.Нед,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ВТ_Индекс.Мес,
ВТ_Индекс.Нед,
ВТ_Индекс.Пн,
ВТ_Индекс.Вт,
ВТ_Индекс.Ср,
ВТ_Индекс.Чт,
ВТ_Индекс.Пт,
ВТ_Индекс.Сб,
ВТ_Индекс.Вс
ИЗ
ВТ_Индекс КАК ВТ_Индекс
ГДЕ
ВТ_Индекс.Индекс < 0
И ВТ_Индекс.Нед - НЕДЕЛЯ(ВТ_Индекс.Мес) - (НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ВТ_Индекс.Мес, ДЕНЬ, -1), МЕСЯЦ, -1)) - НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(ВТ_Индекс.Мес, МЕСЯЦ, -2))) > 0
УПОРЯДОЧИТЬ ПО
Г,
Мес,
Нед,
Пн
Вот, собственно и всё
Замечу, что генератор чисел и последующая генерация списка дат – замечательная вещь. Есть конечно Дополнение периода в СКД и Выбрать(,, «ВСЕ») из запроса, но это работает в конце и не позволяет вести дальнейшие вычисления в запросе, а здесь… хоть куда.
Далее в самом отчете используется Условное оформление, что бы скрыть повторный вывод названий месяца и ненужных номеров недель, ещё там выравнивания и шрифты всякие, ну и конечно Вариант с группировкой по годам.
Прилагаю новый отчет и пошаговый список запросов для консоли.
Надеюсь было полезно, спасибо тем, кто дочитал:)