Появилась задача вычислить возраст человека на определенную дату. Сначала был написан вариант кодом, через цикл, но замер производительности показал, что подобное решение «тяжеловесное». Более того, я знаю, что задача будет повторяться, причем данные нужно будет выдавать запросом. Было решено сделать определенный шаблон, на основании которого можно было бы писать другие решения.
Сначала хорошим решением мне показались запросы в этой публикации. Но тестируя на разные случаи (29.02.2025 и 28.02.2025 и тому подобное) получал не совсем верные результаты. + мне сложно было вникнуть во всех хитросплетения и условия, поэтому решил написать свой вариант:
ВЫБРАТЬ
ГОД(&БольшаяДата) = ГОД(&МеньшаяДата) КАК ГодаРавны,
МЕСЯЦ(&БольшаяДата) > МЕСЯЦ(&МеньшаяДата) КАК МесяцБольше,
МЕСЯЦ(&БольшаяДата) = МЕСЯЦ(&МеньшаяДата) КАК МесяцыРавны,
ДЕНЬ(&БольшаяДата) > ДЕНЬ(&МеньшаяДата) КАК ДеньБольше,
ДЕНЬ(&БольшаяДата) = ДЕНЬ(&МеньшаяДата) КАК ДниРавны
ПОМЕСТИТЬ Условия
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Условия.ГодаРавны КАК ГодаРавны,
Условия.МесяцБольше КАК МесяцБольше,
Условия.МесяцыРавны КАК МесяцыРавны,
Условия.ДеньБольше КАК ДеньБольше,
Условия.ДниРавны КАК ДниРавны,
ДЕНЬ(КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&БольшаяДата, МЕСЯЦ, -1), МЕСЯЦ)) КАК ПоследнийДеньПредыдущегоМесяца,
ДЕНЬ(КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&БольшаяДата, МЕСЯЦ, -1), МЕСЯЦ)) < ДЕНЬ(&МеньшаяДата) И ДЕНЬ(&БольшаяДата) < 3 КАК ДеньПредыдущегоМесяцаМеньше,
Условия.ДеньБольше ИЛИ Условия.ДниРавны КАК ДеньБольшеИлиРавен,
Условия.МесяцБольше ИЛИ Условия.МесяцыРавны КАК МесяцБольшеИлиРавен,
Условия.ДеньБольше ИЛИ Условия.ДниРавны КАК ПоследнийМесяцПрошел,
Условия.МесяцБольше ИЛИ Условия.МесяцыРавны И (Условия.ДеньБольше ИЛИ Условия.ДниРавны) КАК ПоследнийГодПрошел
ПОМЕСТИТЬ СложныеУсловия
ИЗ
Условия КАК Условия
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВЫБОР
КОГДА СложныеУсловия.ГодаРавны
ТОГДА 0
КОГДА СложныеУсловия.ПоследнийГодПрошел
ТОГДА РАЗНОСТЬДАТ(&МеньшаяДата, &БольшаяДата, ГОД)
ИНАЧЕ РАЗНОСТЬДАТ(&МеньшаяДата, &БольшаяДата, ГОД) - 1
КОНЕЦ КАК Лет,
ВЫБОР
КОГДА СложныеУсловия.ПоследнийМесяцПрошел И СложныеУсловия.МесяцБольшеИлиРавен
ТОГДА МЕСЯЦ(&БольшаяДата) - МЕСЯЦ(&МеньшаяДата)
КОГДА СложныеУсловия.МесяцБольше
ТОГДА МЕСЯЦ(&БольшаяДата) - МЕСЯЦ(&МеньшаяДата) - 1
КОГДА СложныеУсловия.ПоследнийМесяцПрошел
ТОГДА 12 - (МЕСЯЦ(&МеньшаяДата) - МЕСЯЦ(&БольшаяДата))
ИНАЧЕ 12 - (МЕСЯЦ(&МеньшаяДата) - МЕСЯЦ(&БольшаяДата) + 1)
КОНЕЦ КАК Месяцев,
ВЫБОР
КОГДА СложныеУсловия.МесяцБольшеИлиРавен И СложныеУсловия.ДеньБольшеИлиРавен
ТОГДА ДЕНЬ(&БольшаяДата) - ДЕНЬ(&МеньшаяДата)
КОГДА СложныеУсловия.ПоследнийМесяцПрошел И СложныеУсловия.ДеньБольшеИлиРавен
ТОГДА ДЕНЬ(&БольшаяДата) - ДЕНЬ(&МеньшаяДата)
КОГДА СложныеУсловия.ДеньПредыдущегоМесяцаМеньше ТОГДА 0
ИНАЧЕ СложныеУсловия.ПоследнийДеньПредыдущегоМесяца - (ДЕНЬ(&МеньшаяДата) - ДЕНЬ(&БольшаяДата))
КОНЕЦ КАК Дней
ИЗ
СложныеУсловия КАК СложныеУсловия
Возможно такой вариант кому-то покажется очевиднее и понятнее. Тестировались различные вариации дат. При этом здесь есть следующее допущение: БольшаяДата всегда больше.
Сверялся с этим онлайн калькулятором, но если кто-то найдет ошибку — жду в комментарии!
Там первый комментарий.
А есть замеры улучшения?
Куда удобнее иметь один универсальный клиент-серверный метод, чем при любом изменении даты рождения в поле на форме, делать серверный вызов, чтоб узнать возраст
(1) да, типа проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:
25 0 29 12.04.1993 0:00:00
что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?
(2) для меня не было критично, т.к. возраст вычислялся при проведении, т.е. на сервере.
при проведении и 5-разовом использовании метода с циклом замер показывал около 92% от всего времени. правда там и нет «тяжеловесных» операций.
тем не менее разность дат там были маленькие, поэтому циклы не были длительными.
сейчас все вычисление занимает около 30% времени. при этом используется моветон — запрос в цикле. и для пяти дат запрос выполняется 5 раз. преимущество — запрос я перепишу, чтобы передавать просто массивы дат и выполнять запрос 1 раз. с вычислением в цикле такой фокус не получится. разве что выполнять вычисления параллельно, запуская фоновые задания, например.
(1) запрос видимо проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:
25 0 29 12.04.1993 0:00:00
что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?
Показать
(6) круто! конечно в хитросплетениях «почему и зачем что-то добавляется» придется разобраться, но компактный и почти рабочий. внесите еще поправку для случая: Д1 = 31.01.2019 и Д2 = 01.03.2019. сейчас выдает:
-2 13 -1
если это поправится — отличный вариант, для тех кто любит компактность
(7) хотя нет. запрос надо пересмотреть. ошибка носит постоянный характер:
(4) Без замеров сложно оценить целесообразность улучшения.
Мы все помним фразу «Преждевременная оптимизация — корень всех зол».
И если было 0.05 сек, а стало 0.01 сек, стоит ли это того, чтобы улучшать и тратить время, разве нету более проблемных мест в других кусках конфигурации?
Ваши 92% совершенно ни о чем не говорят.
(9) целесообразность была в том, чтобы был определенный правильный шаблон, для вычисления возраста именно запросом для использования в будущем.
одна из предполагаемых задач — выдавать список из 500 человек указывая возраст в годах и месяцах на текущую дату. есть разница между 1. рассчитать возраст сразу средствами SQL
2. перебрать все строки выборки/выгрузки, делая вычисления для каждого.
также мне тут напомнили, что сервер предприятия и SQL сервер могут физически на разном железе находиться.
но для локальной задачи «вычислять возраст на сегодня на форме объекта» вполне может хватить и вычисления циклом на клиенте, не спорю.
Спасибо, взял на заметку