gifts2017

Календарь на СКД. Описание запроса

Опубликовал Юрий Зайцев (Yury1001) в раздел Программирование - Практика программирования

Отчет выводит календарь за любой период, по три месяца в ряд, можно за несколько лет подряд. Всё в один запрос. Подробное описание запроса.

Эта версия благодаря поддержке сообщества переделана, так сказать без единого гвоздя: один набор данных запрос и один параметр СтандартныйПериод, модуля у отчета нет, на выходе готовый календарь.

Напомню, что предыдущая версия имеет модуль с процедурой ПриКомпоновкеРезультата и остаётся полезной как пример программной работы с компоновкой – источник данных внешний набор данных Результат запроса, а также как пример работы с запросом - источник данных для запроса Таблица значений, переданная параметром.

Набор данных запрос этого отчета полностью самостоятельный, а отдельные его части могут быть полезны в решении различных нестандартных задач.

Корректно работает только период целыми кварталами!

Итак описание: 

// Объединяем все числа русского языка:)

ВЫБРАТЬ
               
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

УПОРЯДОЧИТЬ ПО
               
Г,
               
Мес,
               
Нед,
               
Пн

 

Вот, собственно и всё

Замечу, что генератор чисел и последующая генерация списка дат – замечательная вещь. Есть конечно Дополнение периода в СКД и Выбрать(,, «ВСЕ») из запроса, но это работает в конце и не позволяет вести дальнейшие вычисления в запросе, а здесь… хоть куда.

 

Далее в самом отчете используется Условное оформление, что бы скрыть повторный вывод названий месяца и ненужных номеров недель, ещё там выравнивания и шрифты всякие, ну и конечно Вариант с группировкой по годам.

Прилагаю новый отчет и пошаговый список запросов для консоли.

Надеюсь было полезно, спасибо тем, кто дочитал:)

Скачать файлы

Наименование Файл Версия Размер
Календарь на СКД 85
.erf 8,84Kb
29.12.11
85
.erf 8,84Kb Скачать
Список запросов (.sel) 26
.sel 31,41Kb
29.12.11
26
.sel 31,41Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Головаченко Дима (Smaylukk) 30.12.11 01:15
Молодец. Очень детально все описано. Хотя, если чесно все равно до конца не понятен весь механизм - это нужно разбирать запрос на части в Предприятии и смотреть, что он делает :D
2. Сергей Ожерельев (Поручик) 30.12.11 01:56
3. Александр Зубцов (iov) 31.12.11 01:08
А для каких целей данный труд был написан?
P.S. Подозреваю что можно применить но пока не могу понять где
4. Юрий Зайцев (Yury1001) 31.12.11 10:55
(3) Генерация ряда чисел, а так же дат может помочь организовать некоторую базу для наложения неполных данных и проведения дальнейших вычислений в запросе.
А сложить данные в две колонки меня за 10 лет просили уже очень много раз (ну на семерке конечно) это и долги для агентов, и остатки там всякие, сводный отчет руководителю, не говоря уже про прайсы.
Да и вообще умение работать с запросом, не побоюсь сказать, самое главное в программировании 1С 8, это и отчеты, и проведение, автозаполнения разные, источник для СКД, и т.д.

Сумел построить хороший запрос – решил 70% задачи!
5. Юрий Зайцев (Yury1001) 31.12.11 10:59
(3) Выделение целой части от деления и остатка, в своё время искал как, прямых функций нет, а здесь реализовано. В общем, это как урок по запросам, уровень начальный средний.
6. WarLex (WarLex) 31.12.11 11:26
Забавная публикация, особенно восхищает скупость используемых параметров и про «Синус Хэ» - очень понравилось - зарубку на столе сделаю, чтоб не забыть, короче, автор, ты - молодец. Главное времени не пожалел на такие изыскания.
7. Александр Зубцов (iov) 31.12.11 12:27
(4)(5) Не это понятно. Я имел ввиду именно это для чего было написано? Была практическая задача или просто творческий порыв?
8. Юрий Зайцев (Yury1001) 31.12.11 12:49
(7) А, ну здесь порыв, предновогодний, творческий, одна штука:)
Захотелось побороть, что ни будь этакое.
9. Александр Зубцов (iov) 31.12.11 12:51
(8) Лови плюс в карму. Творческий прорыв - это хорошо- это гуд... Побольше тебе их в новом году и чтоб финансовый вопрос не мешал творчеству.
10. Alexandr (maloi_a) 01.01.12 14:53
Оригинально!
"Объединяем все числа русского языка".
11. AND AND (AAndryA) 03.01.12 09:35
Интересный результат творческого порыва ! Осталось придумать где использовать.
12. igor_gk (igor_gk) 03.01.12 11:55
13. Ranika (Ranika) 04.01.12 02:32
Работа сделана большая, алгоритм видимо прорабатывался не раз. Молодец
14. Алексей (Alex) 04.01.12 18:03
15. Екатерина Соколова (catena) 05.01.12 06:15
16. Николай Больсунов (boln) 07.01.12 12:11
За мастерскую работу с запросами - плюс однозначно.

А как альтернатива без запроса и без СКД - вот (к сожалению, 8.1):
http://nashe1c.ru/materials-view.jsp?id=290

Объединим усилия? :)
17. Ярослав Юнка (y22-k) 10.01.12 11:03
18. Вячеслав Петрович (kozlovvp) 27.01.12 16:30
Хорошее знание языка запросов! 5+ за знание русского языка))) "// Объединяем все числа русского языка:)"
19. Олег Хугаев (Kov495) 05.10.13 13:50
Применимы только 2-3 начальных запроса и только в качестве построения числовых индексов.
Но это самому написать не составит никакого труда - дел на 3 минуты.
Остальное раскидано интересно, но такой код очень не стабилен и не универсален на произвольном периоде.
20. Владимир Клименко (KliMich) 27.10.13 13:19
Красиво! Отличное владение запросами!
Однозначно плюс.