Maria 데이터베이스로 잡X리아를 보며 설계 연습을 하고 있습니다.
일단 회사 구인 공고 리스트 페이지를 보고 이런식으로 가져와야 겠다라고 생각해서 만들어보았는데요..
(완전 똑같게하진 않았습니다)
TB_COMPANY (회사)
SEQ | NAME | TEL | ADDRESS | REG_TIME
1 | 한국상사 | 0212341234 | 서울특별시 마포구 .... | 2013-03-11 12:11:11
TB_RECRUIT (모집)
SEQ | COMPANY_SEQ | TITLE | CONTENT | RECRUIT_DEADLINE (마감 일시) | REG_TIME
1 | 1 | 디자이너 구해요~ | (*)$#@*&(@# | 2019-07-03 23:59:59 | 2019-06-26 12:23:11
...
TB_CAREER_CD (경력)
CD | NAME | POSITION
FR | 신입 | 1
1Y | 1년 | 2
..
TB_JOB_TYPE_CD (고용 형태)
CD | NAME | POSITION
PT | 아르바이트 | 1
CW | 계약직 | 2
...
TB_RECRUIT_CAREER (모집 - 경력 매핑 테이블, 한 모집 당 여러 경력을 원할 수 있다고 생각)
RECRUIT_SEQ | CAREER_CD
1 | FR
1 | 1Y
...
TB_RECRUIT_JOB_TYPE (모집 - 고용 형태 매핑 테이블, 한 모집 당 여러 고용 형태를 제시할 수 있다고 생각)
RECRUIT_SEQ | JOB_TYPE_CD
1 | CW
이제 구인 공고 리스트에서는 회사명, 회사 주소, 고용 형태, 경력을 보여주면 되겠다고 생각했습니다.
먼저 모집과 회사를 조인했습니다.
SELECT R.TITLE, R.CONTENT, R.REG_TIME, C.NAME, C.ADDRESS FROM TB_RECRUIT R INNER JOIN TB_COMPANY C ON R.COMPANY_SEQ = C.SEQ ORDER BY R.SEQ DESC LIMIT 20;
이제 해당 모집에서 원하는 경력을 보여주려 합니다.
만일 경력을 신입 또는 2년이라고 표시하려면 GROUP_CONCAT을 써야한다고 생각했습니다.
SELECT R.TITLE, R.CONTENT, R.REG_TIME, C.NAME, C.ADDRESS, RC.CAREERS FROM TB_RECRUIT R INNER JOIN TB_COMPANY C ON R.COMPANY_SEQ = C.SEQ INNER JOIN ( SELECT RC.RECRUIT_SEQ, GROUP_CONCAT(C.NAME) AS CAREERS FROM TB_RECRUIT_CAREER RC INNER JOIN TB_CAREER_CD C ON RC.CAREER_CD = C.CD GROUP BY RC.RECRUIT_SEQ ) RC ON RC.RECRUIT_SEQ = R.SEQ ORDER BY R.SEQ DESC LIMIT 20;
이제 고용 형태가 아르바이트 또는 계약직 이런식으로 표시하려면 방금 경력을 추가한 것처럼 조인을 한번 더 하게 됩니다.
만일 공고 관련하여 다른 옵션이 추가된다면 (복지같은) 조인하는 테이블은 계속 늘어나겠죠..
그래서 다른 한편으로는 그냥 TB_RECRUIT 테이블에 CAREERS, JOB_TYPES 컬럼을 추가하여
코드 값을 저장하지 않고 해당 코드의 NAME 값을 GROUOP_CONCAT으로 묶어 저장할까 했습니다.
TB_RECRUIT (모집)
SEQ | COMPANY_SEQ | TITLE | CONTENT | RECRUIT_DEADLINE (마감 일시) | REG_TIME | CAREERS | JOB_TYPES
1 | 1 | 디자이너 구해요~ | (*)$#@*&(@# | 2019-07-03 23:59:59 | 2019-06-26 12:23:11 | 신입, 1년, 2년 | 계약직, 정규직
하지만 이렇게되면 코드 값을 참조하지 않기 때문에 코드 자체가 없어지거나 변경되면 반영이 되지 않으므로 관계형 데이터베이스에서는 맞지 않다고 생각합니다..ㅠ
하지만 불필요한 조인을 줄일 수 있겟죠..
결론은 제 설계가 잘못된거 같은데..어떻게 하면 좀 더 개선시킬 수 있을까요?
당연히 원칙도 정답도 아닌 개인적으로 드리는 가이드입니다.
적절히 취사선택하시기 바랍니다.
1. 코드의 key, valule 쌍을 테이블로 정의해 두는 것은 필요할 수 있지만, 이런 테이블을 서비스에서 직접 JOIN 하여 value를 가져오는 것은 성능면에서 별로 권하고 싶지 않습니다. DB에는 key만 저장하고 key와 쌍을 이루는 value는 웹 서버가 별도의 데이터 구조체 - json 파일이라던가 - 를 가지고 있게해서 그 곳으로부터 value를 추출하게 하면 됩니다.
2. 1:n의 성격을 가지는 속성의 설계는.. TB_RECRUIT_CAREER 같은 매핑 테이블을 사용할 수도 있고, TB_RECRUIT 테이블에서 컬럼으로 관리할 수도 있습니다.
어느 쪽을 선택할 지 판단하는 기준 한 가지는 "검색 필터로 사용될 일이 있는가?" 입니다.
본문에 나오는 경력이나 고용형태는 채용사이트에서 검색할 때 사용하는 기본 필터 중 하나이기 때문에 WHERE 절에서 사용되는 성격이니 정규화해야겠네요.
그럼 목록을 SELECT 할 때마다 GROUP_CONCAT() 해야 하는 상황은 어떻게 해결해야 할까요?
채용 공고는 한 번 올라가면 수정 자체가 빈번한 작업은 아닙니다. 이런 경우에는 정규화와 역정규화를 함께 해 줍니다.
즉, 검색과 무결성 보장을 위한 별도의 매핑 테이블도 만들고 목록을 SELECT할 때 발생하는 그룹핑과 조인을 피하기 위해 그 결과를 미리 컬럼으로도 가지고 있는거죠. 개인적으로 이런 컬럼은 JSON 으로 저장합니다. 이때 주의할 점은 당연히 트랜잭션 처리겠죠. 매핑 테이블의 데이터와 JSON으로 저장한 데이터가 일치하도록 신경 써 줘야 합니다.