Альтернативный вариант расчета возраста (лет, месяцев, дней) запросом

11.04.19

Разработка - Запросы

Появилась задача вычислить возраст человека на определенную дату. Сначала был написан вариант кодом, через цикл, но замер производительности показал, что подобное решение "тяжеловесное". Более того, я знаю, что задача будет повторяться, причем данные нужно будет выдавать запросом. Было решено сделать определенный шаблон, на основании которого можно было бы писать другие решения.

Сначала хорошим решением мне показались запросы в этой публикации. Но тестируя на разные случаи (29.02.2016 и 28.02.2017 и тому подобное) получал не совсем верные результаты. + мне сложно было вникнуть во всех хитросплетения и условия, поэтому решил написать свой вариант:

ВЫБРАТЬ
	ГОД(&БольшаяДата) = ГОД(&МеньшаяДата) КАК ГодаРавны,
	МЕСЯЦ(&БольшаяДата) > МЕСЯЦ(&МеньшаяДата) КАК МесяцБольше,
	МЕСЯЦ(&БольшаяДата) = МЕСЯЦ(&МеньшаяДата) КАК МесяцыРавны,
	ДЕНЬ(&БольшаяДата) > ДЕНЬ(&МеньшаяДата) КАК ДеньБольше,
	ДЕНЬ(&БольшаяДата) = ДЕНЬ(&МеньшаяДата) КАК ДниРавны
ПОМЕСТИТЬ Условия
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Условия.ГодаРавны КАК ГодаРавны,
	Условия.МесяцБольше КАК МесяцБольше,
	Условия.МесяцыРавны КАК МесяцыРавны,
	Условия.ДеньБольше КАК ДеньБольше,
	Условия.ДниРавны КАК ДниРавны,
	ДЕНЬ(КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&БольшаяДата, МЕСЯЦ, -1), МЕСЯЦ)) КАК ПоследнийДеньПредыдущегоМесяца,
	ДЕНЬ(КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&БольшаяДата, МЕСЯЦ, -1), МЕСЯЦ)) < ДЕНЬ(&МеньшаяДата) И ДЕНЬ(&БольшаяДата) < 3 КАК ДеньПредыдущегоМесяцаМеньше,
	Условия.ДеньБольше ИЛИ Условия.ДниРавны КАК ДеньБольшеИлиРавен,
	Условия.МесяцБольше ИЛИ Условия.МесяцыРавны КАК МесяцБольшеИлиРавен,
	Условия.ДеньБольше ИЛИ Условия.ДниРавны КАК ПоследнийМесяцПрошел,
	Условия.МесяцБольше ИЛИ Условия.МесяцыРавны И (Условия.ДеньБольше ИЛИ Условия.ДниРавны) КАК ПоследнийГодПрошел
ПОМЕСТИТЬ СложныеУсловия
ИЗ
	Условия КАК Условия
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВЫБОР
		КОГДА СложныеУсловия.ГодаРавны
			ТОГДА 0
		КОГДА СложныеУсловия.ПоследнийГодПрошел
			ТОГДА РАЗНОСТЬДАТ(&МеньшаяДата, &БольшаяДата, ГОД)
		ИНАЧЕ РАЗНОСТЬДАТ(&МеньшаяДата, &БольшаяДата, ГОД) - 1
	КОНЕЦ КАК Лет,
	ВЫБОР
		КОГДА СложныеУсловия.ПоследнийМесяцПрошел И СложныеУсловия.МесяцБольшеИлиРавен
			ТОГДА МЕСЯЦ(&БольшаяДата) - МЕСЯЦ(&МеньшаяДата)
		КОГДА СложныеУсловия.МесяцБольше
			ТОГДА МЕСЯЦ(&БольшаяДата) - МЕСЯЦ(&МеньшаяДата) - 1
		КОГДА СложныеУсловия.ПоследнийМесяцПрошел
			ТОГДА 12 - (МЕСЯЦ(&МеньшаяДата) - МЕСЯЦ(&БольшаяДата))
		ИНАЧЕ 12 - (МЕСЯЦ(&МеньшаяДата) - МЕСЯЦ(&БольшаяДата) + 1)
	КОНЕЦ КАК Месяцев,
	ВЫБОР
		КОГДА СложныеУсловия.МесяцБольшеИлиРавен И СложныеУсловия.ДеньБольшеИлиРавен
			ТОГДА ДЕНЬ(&БольшаяДата) - ДЕНЬ(&МеньшаяДата)
		КОГДА СложныеУсловия.ПоследнийМесяцПрошел И СложныеУсловия.ДеньБольшеИлиРавен
			ТОГДА ДЕНЬ(&БольшаяДата) - ДЕНЬ(&МеньшаяДата)
		КОГДА СложныеУсловия.ДеньПредыдущегоМесяцаМеньше ТОГДА 0
		ИНАЧЕ СложныеУсловия.ПоследнийДеньПредыдущегоМесяца - (ДЕНЬ(&МеньшаяДата) - ДЕНЬ(&БольшаяДата))
	КОНЕЦ КАК Дней	 		
ИЗ
	СложныеУсловия КАК СложныеУсловия

Возможно такой вариант кому-то покажется очевиднее и понятнее. Тестировались различные вариации дат. При этом здесь есть следующее допущение: БольшаяДата всегда больше.

Сверялся с этим онлайн калькулятором, но если кто-то найдет ошибку - жду в комментарии!

запрос лет месяцев дней возраст

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

12000 руб.

02.09.2020    169256    937    403    

905

Запросы Программист Бесплатно (free)

Увидел cheatsheet по SQL и захотелось нарисовать подобное, но про запросы.

18.10.2024    11390    sergey279    18    

65

Запросы Программист Платформа 1С v8.3 Запросы Конфигурации 1cv8 Бесплатно (free)

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    6338    XilDen    36    

83

Запросы Программист Запросы Бесплатно (free)

Отлаживая взаимодействие с базой данных, мы регулярно сталкиваемся с зависающими или подозрительно долго выполняющимися обращениями, негативно влияющими на производительность. О том, как в PostgreSQL выявить подозрительные запросы, основываясь на доступной о них информации, расскажем в статье.

16.08.2024    9066    user1840182    5    

28

Математика и алгоритмы Запросы Программист Платформа 1С v8.3 Запросы Бесплатно (free)

Рассмотрим быстрый алгоритм поиска дублей с использованием hash функции по набору полей шапки и табличных частей.

08.07.2024    2727    ivanov660    9    

22

Запросы СКД Программист Стажер Система компоновки данных Россия Бесплатно (free)

Часто при разработке отчетов в СКД возникает ситуация, когда не совсем понятно, почему отчет выводит не те данные, которые нужны, либо не выводит вовсе. Возникает потребность увидеть конечный запрос, который формирует СКД. Как это сделать, рассмотрим в этой статье.

15.05.2024    10218    implecs_team    6    

48

Запросы Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    3623    andrey_sag    10    

38
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. user-z99999 72 11.04.19 11:42 Сейчас в теме
5. lex_hrabovskyi 7 11.04.19 12:08 Сейчас в теме
(1) запрос видимо проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:
25 0 29 12.04.1993 0:00:00
что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?
2. Agregadus 46 11.04.19 11:44 Сейчас в теме
А есть замеры улучшения?

Куда удобнее иметь один универсальный клиент-серверный метод, чем при любом изменении даты рождения в поле на форме, делать серверный вызов, чтоб узнать возраст
4. lex_hrabovskyi 7 11.04.19 12:06 Сейчас в теме
(2) для меня не было критично, т.к. возраст вычислялся при проведении, т.е. на сервере.
при проведении и 5-разовом использовании метода с циклом замер показывал около 92% от всего времени. правда там и нет "тяжеловесных" операций.
тем не менее разность дат там были маленькие, поэтому циклы не были длительными.
сейчас все вычисление занимает около 30% времени. при этом используется моветон - запрос в цикле. и для пяти дат запрос выполняется 5 раз. преимущество - запрос я перепишу, чтобы передавать просто массивы дат и выполнять запрос 1 раз. с вычислением в цикле такой фокус не получится. разве что выполнять вычисления параллельно, запуская фоновые задания, например.
9. Agregadus 46 11.04.19 12:38 Сейчас в теме
(4) Без замеров сложно оценить целесообразность улучшения.
Мы все помним фразу "Преждевременная оптимизация — корень всех зол".
И если было 0.05 сек, а стало 0.01 сек, стоит ли это того, чтобы улучшать и тратить время, разве нету более проблемных мест в других кусках конфигурации?
Ваши 92% совершенно ни о чем не говорят.
10. lex_hrabovskyi 7 11.04.19 12:47 Сейчас в теме
(9) целесообразность была в том, чтобы был определенный правильный шаблон, для вычисления возраста именно запросом для использования в будущем.
одна из предполагаемых задач - выдавать список из 500 человек указывая возраст в годах и месяцах на текущую дату. есть разница между 1. рассчитать возраст сразу средствами SQL
2. перебрать все строки выборки/выгрузки, делая вычисления для каждого.
также мне тут напомнили, что сервер предприятия и SQL сервер могут физически на разном железе находиться.

но для локальной задачи "вычислять возраст на сегодня на форме объекта" вполне может хватить и вычисления циклом на клиенте, не спорю.
3. lex_hrabovskyi 7 11.04.19 11:57 Сейчас в теме
(1) да, типа проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:
25 0 29 12.04.1993 0:00:00
что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?
Прикрепленные файлы:
6. duhh 238 11.04.19 12:11 Сейчас в теме
ВЫБРАТЬ
	ВЫБОР
		КОГДА ДЕНЬ(&Д1) > ДЕНЬ(&Д2)
			ТОГДА ДЕНЬ(КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, МЕСЯЦ, -1), МЕСЯЦ)) - ДЕНЬ(&Д1) + ДЕНЬ(&Д2)
		ИНАЧЕ ДЕНЬ(&Д2) - ДЕНЬ(&Д1)
	КОНЕЦ КАК Дней,
	ВЫБОР
		КОГДА ДОБАВИТЬКДАТЕ(&Д2, ГОД, ГОД(&Д2) * -1) < ДОБАВИТЬКДАТЕ(&Д1, ГОД, ГОД(&Д1) * -1)
			ТОГДА 12 - (МЕСЯЦ(&Д1) - МЕСЯЦ(&Д2))
		ИНАЧЕ МЕСЯЦ(&Д2) - МЕСЯЦ(&Д1)
	КОНЕЦ - ВЫБОР
		КОГДА ДЕНЬ(&Д1) > ДЕНЬ(&Д2)
			ТОГДА 1
		ИНАЧЕ 0
	КОНЕЦ КАК Месяцев,
	ВЫБОР
		КОГДА ДОБАВИТЬКДАТЕ(&Д2, ГОД, ГОД(&Д2) * -1) < ДОБАВИТЬКДАТЕ(&Д1, ГОД, ГОД(&Д1) * -1)
			ТОГДА ГОД(&Д2) - ГОД(&Д1) - 1
		ИНАЧЕ ГОД(&Д2) - ГОД(&Д1)
	КОНЕЦ КАК Лет
Показать
7. lex_hrabovskyi 7 11.04.19 12:20 Сейчас в теме
(6) круто! конечно в хитросплетениях "почему и зачем что-то добавляется" придется разобраться, но компактный и почти рабочий. внесите еще поправку для случая: Д1 = 31.01.2019 и Д2 = 01.03.2019. сейчас выдает:
-2 13 -1

если это поправится - отличный вариант, для тех кто любит компактность
8. lex_hrabovskyi 7 11.04.19 12:28 Сейчас в теме
(7) хотя нет. запрос надо пересмотреть. ошибка носит постоянный характер:
Прикрепленные файлы:
11. pun4er 16.04.19 13:32 Сейчас в теме
Спасибо, взял на заметку
12. riposte 391 21.04.19 15:36 Сейчас в теме
Недавно на форуме 1С натыкался и давал ответ. Без всяких циклов.

Процедура ПоказатьРазностьДатНажатие(Элемент)
    Если НЕ ЗначениеЗаполнено(ДатаНачала) ИЛИ НЕ ЗначениеЗаполнено(ДатаКонца) Тогда
        ЭлементыФормы.Н_РазностьСтрокой.Заголовок = "Не указана дата";
        ЭлементыФормы.Н_РазностьСтрокой.ЦветТекста = ЦветаСтиля.ЦветОтрицательногоЧисла;
        Возврат;
    КонецЕсли;
    ЭлементыФормы.Н_РазностьСтрокой.ЦветТекста = ЦветаСтиля.ПоясняющийТекст;
    
    РазностьДат = ДатаКонца - ДатаНачала;
    Мод = ?(РазностьДат < 0, -1, 1);  // Модификатор
    // Если Мод > 0 - значит ДатаКонца - будущее, иначе - прошлое и считать будем в обратную сторону.
    
    Лет = 0;
    Месяцев = 0;
    Дней = 0;
    
    Если Мод > 0 Тогда
        РазобратьРазностьДат(НачалоДня(ДатаКонца), НачалоДня(ДатаНачала), Лет, Месяцев, Дней);
    Иначе
        РазобратьРазностьДат(НачалоДня(ДатаНачала), НачалоДня(ДатаКонца), Лет, Месяцев, Дней);
    КонецЕсли;
    Дней = Дней - 1;    
    
    Если Мод > 0 Тогда
        РазницаЧМС = (КонецДня(ДатаНачала)+1 - ДатаНачала) + (ДатаКонца - НачалоДня(ДатаКонца)); 
    Иначе
        РазницаЧМС = (КонецДня(ДатаКонца)+1 - ДатаКонца) + (ДатаНачала - НачалоДня(ДатаНачала));
    КонецЕсли;
    
    Если РазницаЧМС >= 86400 Тогда
        Дней = Дней + 1;
           РазницаЧМС = РазницаЧМС - 86400;
    КонецЕсли;
    Часов = ?(РазницаЧМС >= 3600, Цел(РазницаЧМС / 3600), 0);
    РазницаЧМС = РазницаЧМС - Часов * 3600;
    
    Минут = ?(РазницаЧМС >= 60, Цел(РазницаЧМС / 60), 0);
    РазницаЧМС = РазницаЧМС - Минут * 60;
    Секунд = РазницаЧМС;
    
    СО = Новый Структура; // Структура ответа
    СО.Вставить("Префикс", ?(Мод < 0, "Прошло", "Наступит через"));
    СО.Вставить("ЗначЛет", ?(Лет > 0, " " + Лет + " лет", ""));
    СО.Вставить("ЗначМесяцев", ?(Месяцев > 0, " " + Месяцев + " месяцев", ""));
    СО.Вставить("ЗначДней", ?(Дней > 0, " " + Дней + " дней", ""));
    СО.Вставить("ЗначЧасов", ?(Часов > 0, " " + Часов + " часов", ""));
    СО.Вставить("ЗначМинут", ?(Минут > 0, " " + Минут + " минут", ""));
    СО.Вставить("ЗначСекунд", ?(Секунд > 0, " " + Секунд + " секунд", ""));
                
    СтрокаОтвета = "" + СО.Префикс + СО.ЗначЛет + СО.ЗначМесяцев + СО.ЗначДней + СО.ЗначЧасов + СО.ЗначМинут + СО.ЗначСекунд;
    ЭлементыФормы.Н_РазностьСтрокой.Заголовок = СтрокаОтвета;
    
КонецПроцедуры

Процедура РазобратьРазностьДат(Дата1, Дата2, Лет = 0, Месяцев = 0, Дней = 0)
    
    Лет        = 0;
    Месяцев    = 0;
    Дней    = 0;
    Если Дата1 > Дата2 Тогда
        
        ВременнаяДата = Дата1;
        Если День(ВременнаяДата) < День(Дата2) Тогда
            Дней = (ВременнаяДата - ДобавитьМесяц(ВременнаяДата,-1))/86400;
            ВременнаяДата = ДобавитьМесяц(ВременнаяДата,-1);
        КонецЕсли;
        Если Месяц(ВременнаяДата) < Месяц(Дата2) Тогда
            ВременнаяДата = ДобавитьМесяц(ВременнаяДата,-12);
            Месяцев = 12;
        КонецЕсли;
        Лет        = Макс(             Год(ВременнаяДата)        - Год(Дата2),    0);
        Месяцев    = Макс(Месяцев    + Месяц(ВременнаяДата)    - Месяц(Дата2),    0);
        Дней    = Макс(Дней        + День(ВременнаяДата)    - День(Дата2),    0);
        
       // скорректируем отображаемое значение, если "вмешалось" разное количество дней в месяцах
 
        Если Дата2 <> (ДобавитьМесяц(Дата1,-Лет*12-Месяцев)-Дней*86400) Тогда
            Дней = Дней + (День(КонецМесяца(Дата2)) - День(НачалоМесяца(Дата2))) - (День(КонецМесяца(ДобавитьМесяц(Дата1,-1))) - День(НачалоМесяца(ДобавитьМесяц(Дата1,-1))));
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры   // РазобратьРазностьДат
Показать
Оставьте свое сообщение