프로젝트 진행 중 참조 관계인 테이블의 데이터를 SELECT 해오려고 할 때 고민되는 문제가 생겼다.
프론트에서 조건을 받아오면 해당 조건과 일치하는 데이터를 A테이블에서 가져오고, 가져온 튜플들의 id를 가지고 A와 참조 관계에 있는 B테이블의 데이터를 또 가져와야 했다.
언제나 그렇듯이 정답은 있다!!!!
하나의 컬럼에는 원자값만이 존재할 수 있다고 공부해왔는데..
JSON 데이터를 컬럼 하나에 넣는다면 어떨까?!!!
해당 기능은 Oracle, PostgreSQL, MySQL, MariaDB 등 다양한 DBMS 최신 버전에서 지원하고 있다.
나는 MariaDB 기준으로 작성했다.
JSON_EXTRACT(jsonData, path) : 이미 만들어진 jsonData에서 내가 원하는 요소만 볼 수 있도록 한다. JSON_EXTRACT(데이터, '$.job') 으로 최상위 위치에 있는 객체에서 job이라는 컬람의 결과를 확인할 수 있다.JSON_UNQUOTE(jsonData) : 받은 jsonData의 따옴표를 제거한다. 보통 EXTRACT의 결과가 문자열일 때 순수한 문자열로 변환하기 위해 사용한다.JSON_ARRAYAGG(jsonData) : 받은 jsonData를 하나의 배열로 만든다. AGG는 집계함수라는 뜻JSON_OBJECT(key, value...) : 내가 넘겨준 key, value 형태를 가지고 하나의 JSON 객체를 만든다.JSON_ARRAY(1,2...) : 내가 넘겨준 데이터를 가지고 하나의 JSON 배열을 만든다. D 테이블에 있는 내용을 JSON_OBJECT로 만들어서 하나의 배열로 만들어 detail이라는 이름으로 전달했다.
const SQL_SELECT_SCHEDULES = `
SELECT
S.id AS schedulesId,
S.day,
S.start_time AS startTime,
JSON_ARRAYAGG(
JSON_OBJECT(
'detailId', D.id,
'placeId', D.place_id,
'orderBy', D.order_by,
'playTime', D.play_time,
'moveTime', D.move_time
)
) AS detail
FROM schedules S LEFT JOIN schedule_detail D ON S.id = D.schedule_id
WHERE S.map_id = ?
GROUP BY S.id, S.day, S.start_time
ORDER BY S.day
`;
쿼리 result에서 JSON 데이터 사용은 다음과 같다.
JSON_ARRAYAGG의 결과는 문자열 타입으로 반환되어 저장되기 때문에, JSON.parse로 다시 변환하는 작업이 필요했다.
const results = await scheduleService.getSchedules(mapId);
results.forEach((item) => {
item.detail = JSON.parse(item.detail);
});