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

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

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

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

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

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

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

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

12 Comments

  1. user-z99999

    https://infostart.ru/public/616313/

    Там первый комментарий.

    Reply
  2. Agregadus

    А есть замеры улучшения?

    Куда удобнее иметь один универсальный клиент-серверный метод, чем при любом изменении даты рождения в поле на форме, делать серверный вызов, чтоб узнать возраст

    Reply
  3. lex_hrabovskyi

    (1) да, типа проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:

    25 0 29 12.04.1993 0:00:00

    что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?

    Reply
  4. lex_hrabovskyi

    (2) для меня не было критично, т.к. возраст вычислялся при проведении, т.е. на сервере.

    при проведении и 5-разовом использовании метода с циклом замер показывал около 92% от всего времени. правда там и нет «тяжеловесных» операций.

    тем не менее разность дат там были маленькие, поэтому циклы не были длительными.

    сейчас все вычисление занимает около 30% времени. при этом используется моветон — запрос в цикле. и для пяти дат запрос выполняется 5 раз. преимущество — запрос я перепишу, чтобы передавать просто массивы дат и выполнять запрос 1 раз. с вычислением в цикле такой фокус не получится. разве что выполнять вычисления параллельно, запуская фоновые задания, например.

    Reply
  5. lex_hrabovskyi

    (1) запрос видимо проще. а правильнее? я добавил в консоль, поставил: ДР: 12.04.1993, ТД: 11.04.2019 и мне выдало:

    25 0 29 12.04.1993 0:00:00

    что никак неправильно, потому что должно быть 11 месяцев. стоит проверять дальше первый комментарий?

    Reply
  6. duhh
    ВЫБРАТЬ
    ВЫБОР
    КОГДА ДЕНЬ(&Д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)
    КОНЕЦ КАК Лет

    Показать

    Reply
  7. lex_hrabovskyi

    (6) круто! конечно в хитросплетениях «почему и зачем что-то добавляется» придется разобраться, но компактный и почти рабочий. внесите еще поправку для случая: Д1 = 31.01.2019 и Д2 = 01.03.2019. сейчас выдает:

    -2 13 -1

    если это поправится — отличный вариант, для тех кто любит компактность

    Reply
  8. lex_hrabovskyi

    (7) хотя нет. запрос надо пересмотреть. ошибка носит постоянный характер:

    Reply
  9. Agregadus

    (4) Без замеров сложно оценить целесообразность улучшения.

    Мы все помним фразу «Преждевременная оптимизация — корень всех зол».

    И если было 0.05 сек, а стало 0.01 сек, стоит ли это того, чтобы улучшать и тратить время, разве нету более проблемных мест в других кусках конфигурации?

    Ваши 92% совершенно ни о чем не говорят.

    Reply
  10. lex_hrabovskyi

    (9) целесообразность была в том, чтобы был определенный правильный шаблон, для вычисления возраста именно запросом для использования в будущем.

    одна из предполагаемых задач — выдавать список из 500 человек указывая возраст в годах и месяцах на текущую дату. есть разница между 1. рассчитать возраст сразу средствами SQL

    2. перебрать все строки выборки/выгрузки, делая вычисления для каждого.

    также мне тут напомнили, что сервер предприятия и SQL сервер могут физически на разном железе находиться.

    но для локальной задачи «вычислять возраст на сегодня на форме объекта» вполне может хватить и вычисления циклом на клиенте, не спорю.

    Reply
  11. pun4er

    Спасибо, взял на заметку

    Reply
  12. riposte
    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *