기록이 힘이다.

[SQL 쿡북] 8. 날짜 산술 본문

SQL

[SQL 쿡북] 8. 날짜 산술

dev22 2023. 3. 22. 07:49
728x90

기본적인 날짜 산술에 중점을 두고 설명합니다. 다음 장에서는 더 많은 고급 날짜 레시피를 찾아볼 수 있습니다. 

 

8.1 일, 월, 연도 가감하기

8.2 두 날짜 사이의 일수 알아내기

MySQL의 DATEDIFF에는 두 개의 매개변수(차이를 찾을 두 날짜)만 필요하며, 음숫값을 피하고자 두 날짜 중 더 작은 날짜를 먼저 전달해야 합니다.(SQL Server와 반대).

 

8.3 두 날짜 사이의 영업일수 알아내기

휴일도 제외하려면 HOLIDAYS 테이블을 만들어서 처리합니다. 해법에서 간단한 NOT IN 술어를 추가하여 HOLIDAYS에 나열된 날짜를 제외합니다. 

 select max(case when ename = 'BLAKE'
                  then hiredate
            end) as blake_hd,
        max(case when ename = 'JONES'
                 then hiredate
             end) as jones_hd
   from emp
  where ename in ( 'BLAKE','JONES' )
 select sum(case when date_format(
                          date_add(jones_hd,
                                   interval t500.id-1 DAY),'%a')
                    in ( 'Sat','Sun' )
                  then 0 else 1
             end) as days
    from (
  select max(case when ename = 'BLAKE'
                  then hiredate
            end) as blake_hd,
        max(case when ename = 'JONES'
                 then hiredate
             end) as jones_hd
   from emp
  where ename in ( 'BLAKE','JONES' )
        ) x,
        t500
  where t500.id <= datediff(blake_hd,jones_hd)+1

1. 시작 날짜와 종료 날짜 사이의 날짜(포함)을 반환합니다.

2. 주말을 제외하고 며칠(행)이 있는지 계산합니다. 

 

WHERE 절을 살펴보면 BLAKE_HD와 JONES_HD의 차이에 1을 더하여 필요한 30개 행을 생성함을 알 수 있습니다.(그렇지 않으면 29개 행이 생성됩니다.) 또한 ID 값이 1에서 시작하고 JONES_HD에 1을 더하면 JONES_HD가 최종 개수에서 제외될 수 있으므로, 외부 쿼리의 SELECT 목록에 있는 T500.ID에서 1을 빼는 것을 확인 할 수 있습니다.

결과셋에 필요한 행 수를 생성한 후에는 CASE 표현식을 사용하여 반환된 각 요일이 평일인지 주말인지 여부를 표시(평일에는 1을, 주말에는 0을 반환) 마지막 단계는 집계함수 SUM을 사용하여 1인 수를 집계하여 최종 답을 도출합니다.

 

8.4 두 날짜 사이의 월 또는 년 수 알아내기

1년은 항상 12개월이므로 두 날자 사이의 개월 수를 찾은 다음 12로 나누어 연도를 구할 수 있습니다. 해법에 익숙해지면 원하는 연도에 따라 결과를 반올림합니다. 예를 들어 EMP테이블의 첫 번째 HIREDATE는 12-DEC-1980이고 마지막은 12-JAN-1983입니다. 연도(1983-1908)에 대해 계산하면 3년이 되지만, 개월 차이는 (2년이 조금 넘는) 약 25개월입니다. 적합하다고 판단되는 쪽으로 조정해야 합니다. 다음 해법에서는 25개월 및 약 2년을 반환합니다.

 

YEAR 및 MONTH 함수를 사용하여 제공된 날짜에 대해 4자리 연도와 2자리 월을 반환합니다. 

<DB2와 MySQL>
 select mnth, mnth/12
   from (
 select (year(max_hd) - year(min_hd))*12 +
        (month(max_hd) - month(min_hd)) as mnth
   from (
 select min(hiredate) as min_hd, max(hiredate) as max_hd
   from emp
        ) x
        ) y

8.5 두 날짜 사이의 시,분,초 알아내기

 select datediff(allen_hd,ward_hd)*24 hr,
         datediff(allen_hd,ward_hd)*24*60 min,
         datediff(allen_hd,ward_hd)*24*60*60 sec
    from (
  select max(case when ename = 'WARD'
                   then hiredate
             end) as ward_hd,
         max(case when ename = 'ALLEN'
                  then hiredate
            end) as allen_hd
   from emp
        ) x

8.6 1년 중 평일 발생 횟수 계산하기

 

1. 연도에 가능한 모든 날짜를 생성합니다.

2. 해당 요일의 이름이 나오도록 날짜 형식을 지정합니다.

3. 각 요일의 발생 횟수를 셉니다.

<MySQL>
 select date_format(
            date_add(
                cast(
              concat(year(current_date),'-01-01')
                     as date),
                     interval t500.id-1 day),
                     '%W') day,
         count(*)
    from t500
  where t500.id <= datediff(
                          cast(
                        concat(year(current_date)+1,'-01-01')
                               as date),
                          cast(
                        concat(year(current_date),'-01-01')
                               as date))
 group by date_format(
             date_add(
                 cast(
               concat(year(current_date),'-01-01')
                      as date),
                      interval t500.id-1 day),
                      '%W')

테이블 T500에 대해 선택하여 해당 연도의 모든 일을 반환하기에 충분한 행을 생성합니다. DATE_FORMAT 함수를 사용하여 각 날짜의 요일명을 얻은 다음 요일별 발생 횟수를 계산합니다.

 

8.7 현재 레코드와 다음 레코드 간의 날짜 차이 알아내기

 select x.ename, x.hiredate, x.next_hd,
        datediff(x.next_hd, x.hiredate) as diff
   from (
 select deptno, ename, hiredate,
        lead(hiredate)over(order by hiredate) as next_hd
   from emp e
        ) x
  where e.deptno=10

윈도우 함수 LEAD를 사용한 다음, 8.2절에서 설명한 기술을 사용하여 두 날짜 간의 차이를 찾습니다. 

추가적인 조인 없이 현재 행 주변의 행에 액세스하여 더 읽기 쉽고 효율적인 코드를 제공합니다.

 

 

8.8 마치며

날짜는 일반적인 데이터 유형이지만, 단순한 숫자 데이터 유형보다 구조가 더 복잡한 만큼 독특합니다. 핵심 함수들을 마스터하면 날짜를 쉽게 처리할 수 있습니다.