다중 조건 목록 정렬을 위한 MySQL Query if 사용 + datatable

Jiwon Yi·2023년 3월 10일
0

왜?

DB에서 한 테이블의 데이터를 "등록일"을 기준으로 내림차순으로 정렬 해서 보여주는 목록 페이지가 하나 있었다. 이 테이블에는 "시작일"과 "종료일"이라는 필드도 있어서 목록을 표시할 때 둘을 계산해서 "기간"도 같이 표시해주고 있다.
어느날, 이런 요청이 들어왔다.

기간이 설정되지 않은 데이터를 제일 위에 보여주세요.
기간이 설정된 데이터는 그 뒤에 오름차순으로 정렬해주시구요.
기간이 마이너스인 데이터는 플러스인 데이터보다 뒤에 나와야하구요, 이건 내림차순으로 보여주세요.

지금까지 기껏해야 order bydesc,asc나 썼었는데... 순간 '이게 되는 건가?' 싶었지만 안될리가 있나(!), 그냥 내가 모르는 것이었을 뿐!

어떻게?

그 동안에 사용할 일이 없어서 몰랐지만, MySQL Query에서 if 를 쓸 수 있다.
if 문 사용방식은 다음과 같이, Excel 수식 사용 방법과 동일하다.

if (조건, true일 때 값, false일 때 값)

이제 if를 사용해서 데이터 select 할 때, 조건에 따라 정렬을 위한 임시 필드를 만들어주고, 이 임시 필드들을 다시 view에서(나의 경우 jQuery datatable에서) 정렬에 사용하면 된다.

  1. 시작일과 종료일 값이 있으면 차이를 계산하고 없으면 0 값으로 임시 필드 term을 만든다. 이걸로 첫번째 조건인 기간이 설정되지 않은 데이터를 제일 위에 표시 할 수 있다.
  2. 시작일과 종료일 차이를 계산한 값이 양수이면 1, 음수이면 0 값으로 임시 필드 term_sign을 만든다. 이걸 내림차순으로 사용하면 세번째 조건인 기간이 마이너스인 데이터는 플러스보다 뒤에 오도록 할 수 있다.
  3. 시작일과 종료일 차이를 계산한 값을 절대값으로 환산해서 임시 필드 term_abs를 만든다. 이렇게 절대값을 사용하면 기간이 플러스인 경우 오름차순, 마이너스인 경우 내림차순으로 정렬할 수 있다.

주의해야 할 부분
as로 만들어낸 임시 필드는 다른 일반 필드들처럼 조건에 사용할 수 없다.

코드를 보자

이 프로젝트는 Laravel Framework로 되어있다. 주제 자체는 Laravel 특성과는 관계가 없고, 그냥 코드 부분을 볼 때 참고를 위해 적어둔다.

  • Controller
    $data = Payment::select(`id`,`company`, `status`, `start_time`, `end_time`, `created_time`,
    	\DB::raw("if(start_time != 0 && end_time != 0, DATEDIFF(FROM_UNIXTIME(end_time), NOW()), 0) AS term"),
        \DB::raw("if( if(start_time != 0 && end_time != 0, DATEDIFF(FROM_UNIXTIME(end_time), NOW()), 0) >= 0, 1, 0) as term_sign"),
        \DB::raw("if(start_time != 0 && end_time != 0, abs(DATEDIFF(FROM_UNIXTIME(end_time), NOW())), 0) AS term_abs"));
    if를 사용한 각 줄이 위에 적은 1, 2, 3에 각각 대응한다.

위에 주의사항으로 적은 것처럼, as 로 만든 임시 필드인 term을 if 구문에서 사용할 수 없기 때문에 어쩔 수 없이 같은 조건문을 반복해서 사용하고 있다.

  • View
    datatable을 사용하는 script 부분만
     function dataTable({
       dataTable = table.dataTable({
       	...(생략)
           
           columns: [
               { data: 'id' },
               { data: 'company' },
               { data: 'status' },
               { data:  null, orderable:false, searchable:false, 
               		render: function(data) {
                   		if (data.start_time == 0 || data.end_time == 0) return "설정안됨";
                   		return data.start_time + " ~ " + data.end_time + "(" + data.term + ")";
                   }
                },
                { data: 'term_abs', visible:false, searchable:false },
                { data: 'term_sign', visible:false, searchable:false },
           ],
           order: [[5, 'desc'],[4, 'asc']],
           
           ...(후략)
    term_abs, term_sign은 정렬을 위한 필드이기 때문에 visiblefalse로 설정해야 데이터가 표시되지 않는다.
    order 옵션을 다중으로 설정해서 term_sign > term_abs 순으로 정렬이 적용되어 원하는 대로 목록이 표시되었다.
profile
퍼덕거리는 개발자

0개의 댓글