PostgreSQL에서 AGE를 사용하기 전에 두 번 생각하십시오.

3605 단어 sqltime
최근에 Productboard의 친구가 우리 서비스 중 하나에서 흥미로운 버그를 발견했습니다. 어떤 이유로 고객의 기능이 특정 상태(아이디어, 검색, 배송 등)에서 보내는 일수를 계산하는 코드는 경우에 따라 잘못된 결과를 제공합니다.

예를 들어 달력을 보면 2021-02-28 00:00:002022-05-03 00:00:00 사이의 날짜 차이가 429 days임을 알 수 있지만 코드는 해당 기간에 대해 428.25 days를 반환합니다.

약간의 조사 후에 문제는 PostgreSQL의 AGE 기능을 사용하여 날짜 차이를 계산했기 때문에 발생했음이 밝혀졌습니다.

PostgreSQL의 docs AGE 함수에 따르면 “symbolic” result that uses years and months, rather than just days 을 계산합니다.
"상징적"이 무엇을 의미하는지 명확하지 않았기 때문에 조금 파기 시작했고 AGE의 목적이 정확한 시차를 계산하는 것이 아니라 인간이 하는 방식으로 나이를 계산하는 것임을 깨달았습니다. . 컴퓨터처럼 UNIX 타임스탬프를 빼는 대신 날짜의 각 구성 요소를 뺀 다음 음수 값을 조정합니다.

따라서 이전 예(2021-02-28 - 2022-05-03)에서:
  • 년 차이는 1
  • 월 차이는 3입니다.
  • 일수 차이는 -25 이므로 1개월을 빼고 2월이 몇 일 남았는지 확인(0)한 다음 5월부터 날짜를 더합니다(3). 결국 우리는 3 일로 끝납니다.

  • 우리는 1 year, 2 months and 3 days로 끝납니다. 이제 Postgres가 여기에서 반환되는 이유428.25는 무엇입니까?

    왜냐하면:
  • AGE가 반환한 일수는 365.25입니다. 이는 윤년을 고려한 1년의 평균 일수입니다.
  • Postgres는 30를 각 월
  • 의 일수로 사용합니다.

    이제 모든 것이 이해가 됩니다 - 1 year, 2 months and 3 days일은 365.25 + 2 * 30 + 3 = 428.25일 🤓입니다.

    다행히 우리 문제에 대한 해결책은 매우 간단했습니다. AGE 함수를 빼기 연산자로 교체하기만 하면 되었습니다.
    차이점을 보여드리기 위해 온라인postgres query tool에서 실행한 쿼리는 다음과 같습니다.

    SELECT 
        EXTRACT(epoch FROM ('2022-05-03 00:00:00'::timestamp - '2021-02-28 00:00:00'::timestamp)) / (3600 * 24) as subtraction_days, 
        EXTRACT(epoch FROM AGE('2022-05-03 00:00:00'::timestamp, '2021-02-28 00:00:00'::timestamp)) / (3600 * 24) as age_days 
    FROM "current_schema"()
    




    이 이야기의 교훈은 시간 계산이 컴퓨터와 인간이 다르게 접근할 수 있는 극도로 민감한 문제라는 것입니다. 다행스럽게도 PostgreSQL에는 가능한 모든 접근 방식이 포함되어 있습니다. 개발자로서 우리는 사용 사례를 이해하고 문서를 읽고 적절한 것을 선택하기 위해 두 번 생각해야 합니다.

    좋은 웹페이지 즐겨찾기