회사에서 로그 정보를 약 4개의 테이블이 where조건으로 서로 연관되어 조회하고 있었습니다. 기존에는 회원들의 id를 where 조건에 걸어두면 되었지만, 새로운 기능은 비로그인 회원들의 개인정보 열람 기록도 로그에 포함하는 것이었습니다. 안타깝게도 비로그인을 하면 회원 id가 없기 때문에 해당 정보를 where에서 조건을 걸어줄 방법이 없었습니다. 따라서 where절을 모두 join으로 변경했습니다. 많은 테이블의 where조건을 join으로 변경하며 join의 매력에 대해 알아보겠습니다. 성능 이슈는 다루지 않습니다.
*where
where에서만 조건을 건다는 것은, from table1, table2, table3처럼 from에 테이블 정보가 모두 들어간다는 것이니다. 가장 먼저 where는 생각하지 말고 단순히 from만 생각해서 m * n * k의 row를 생성한다는 것에 집중합시다.
-- 테이블 생성
create table backend (
id INT,
name VARCHAR(10)
);
create table front (
id INT,
name VARCHAR(10)
);
create table design (
id INT,
name VARCHAR(10)
);
-- 샘플 데이터 입력
insert into backend (id, name) values (1, 'A');
insert into backend (id, name) values (2, 'B');
insert into backend (id, name) values (3, 'C');
insert into backend (id, name) values (4, 'D');
insert into front (id, name) values (1, 'A');
insert into front (id, name) values (2, 'F');
insert into front (id, name) values (3, 'G');
insert into front (id, name) values (4, 'D');
insert into design (id, name) values (1, 'I');
insert into design (id, name) values (2, 'J');
-- 모든 개발자 조회
select * from backend, front, design;
backend, front, design 에 각각 4개, 4개, 2개의 데이터가 있으므로 모두 출력하면 4 * 4 * 2 = 32개의 row가 생성됩니다. 하나하나씩 열심히 모두 탐색한다고 생각하면 됩니다.
ID NAME ID NAME ID NAME
1 A 1 A 1 I
1 A 2 F 1 I
1 A 3 G 1 I
1 A 4 D 1 I
2 B 1 A 1 I
2 B 2 F 1 I
...
4 D 1 A 2 J
4 D 2 F 2 J
4 D 3 G 2 J
4 D 4 D 2 J
여기서 "백엔드와 프런트가 가능한 풀스택 개발자가 있습니까?" 라고 생각을 하여 where조건을 추가합니다
select * from backend, front, design where backend.name = front.name;
그렇다면 아래와 같이 이름이 같은 칼럼들만 뽑혀져 나옵니다. 즉, 전체 데이터를 쭉~~~~ 나열하고 그중에 원하는 조건에 따라서 row를 뽑아낸다고 생각하면 편합니다.
ID NAME ID NAME ID NAME
1 A 1 A 1 I
1 A 1 A 2 J
4 D 4 D 1 I
4 D 4 D 2 J
중복이 많이 걸리기 때문에 A와 D만 검색하기 위해서는 distinct를 사용한 distinct(backend.name)을 사용하면 훨씬 간편합니다.
*join
저는 복잡한 검색조건을 통해 원하는 정보를 조회해야 하기 때문에 join을 사용했습니다. join은 무조건 아래 그림을 머리에 떠올리고 시작하면 편합니다. 우리가 join을 통해 on을 적어주는 부분이 공통집합임을 미리 알아둡니다. 또한 색칠에 대한 여부는 where가 해결합니다. 사진의 오른쪽 부분에 rigth join은 사용하지 않는데 이유는 나중에 밝히겠습니다.
join은 기본적으로 left join, right join 2개가 있는데 모두 같이 공부해야 할까요? 축구에 left join은 왼발, right join은 오른발에 비유한다면 당연히 left join, right join을 잘 사용하는 양발잡이가 훨씬 다양한 기술을 구사할 것입니다. 하지만 어중간하게 2개를 해서 실수하느니 한쪽 발인 left join을 제대로 알고 가는 것이 더 좋다고 생각합니다.
그래서 저는 웬만큼 left join을 이용하여 해결하려고 노력합니다. 그 다음 join의 핵심은 무엇일까요? 가장 먼저 left join을 사용하기 위해서는 기준 테이블을 잡아야 합니다. 기준 테이블이란 조회에서 중심이 되는 중요한 데이터를 가진 테이블이고, 조회 결과 모든 row가 온전히 기존에 자신의 값을 유지하길 원하는 테이블입니다. 위의 사진에서 보면 left join은 공통집합을 제외하더라도 자신만의 데이터를 온전히 가지고 있습니다. 따라서 기준 테이블의 row를 기준으로 새롭게 발견하고 싶은 데이터가 있으면 on을 통해 공통집합으로 만들면 됩니다. "백엔드 개발자랑... 또 그 중에서 프런트까지 되는 개발자는 누구야?"
select * from backend
left join front
on backend.name = front.name;
기준 테이블은 backend로 잡고 left join은 front를 하겠습니다. 공통집합은 name으로 하기에 on에 사용합니다. 결과를 한번 생각해봅시다. join 이전에 위의 사진에 있는 그림중 어떤 것을 사용할지 머리에 떠올리면 도움이 많이 됩니다. 해당 경우 맨 왼쪽 위에 A영역(backend)이 모두 빨간색으로 칠해진 그림을 떠올립니다. join 방법과 where 방법을 계속 비교해서 본다면 더욱 차이를 잘 알 수 있습니다.
1 A 1 A
4 D 4 D
2 B null null
3 C null null
결과에 null이 등장합니다. 이제 슬슬 머리가 아픕니다. 저도 여기서 join을 여러번 많이 포기했습니다.
하지만 join에서 포인트는 "기준 테이블은 그대로 있고 추가적으로 필요한 테이블을 공통집합에 매칭시킨다"입니다. 놀랍게도 조회 결과 row가 4줄인 것은 backend 테이블이 4개의 row를 가지고 있기 때문입니다. 기준 테이블은 고정이고 join하는 테이블이 옆에 달라붙습니다. (물론 front에서 A라는 이름을 가진 row가 2개라면 기준테이블 backend에서 A라는 이름과 2번 매칭이 되어서 총 5개의 row가 생성됩니다. 테이블이 더 있다면 더 늘어납니다.)
한단계 나아가 봅시다. 반대로, 기준테이블 backend에서 중복된 것을 제외 하고 싶어서 검색한것이라면? 그럴때 무엇을 사용해야 할까요? "프런트는 전혀 모르고 백엔드만 할 줄 아는 사람은 누구야?"(바로 나야!) 네, 이 때 where을 사용합니다. join에서 on으로 조건을 걸었지만 다시 where를 사용할 수 있습니다. 이제 우리는 사진에서 맨 왼쪽 위에서 하나 아래를 떠올립니다. 공통 부분도 빼버리고 순수 기준테이블 backend만 가지는 겁니다. 여기서 다시 한번 알 수 있는 사실은 on은 공통집합을 담당하고 where는 색칠 여부를 담당합니다.
select * from backend
left join front on backend.name = front.name
where front.name is null;
2 B null null
3 C null null
순수 backend 테이블의 칼럼 부분만 빼왔기 때문에 front의 부분은 null이 나올수밖에 없습니다. 즉, "B와 C는 프런트를 못하고 백엔드만 할 줄 압니다~"
두단계 더 나아가 생각해봅시다. 우리가 "on backend.name = front.name"를 해준 이유는 분명 backend 테이블과 front테이블에서 이름이 같은 row를 가지고 조회하고 싶은 것이 있기 때문일 것입니다. 즉, "풀스택 개발자가 누구야?"가 궁금할때는 사진에서 정확히 가운데를 떠올립니다. 여기서는 left join이 아닌 inner join을 사용해서 공통집합만 끄집어 냅니다.
select * from backend
inner join front
on backend.name = front.name;
1 A 1 A
4 D 4 D
2개 테이블은 쉬우니 design을 하나 더 껴서 생각해봅시다. 의미있는 결과를 위해 다음 row를 추가합니다
insert into design (id, name) values (3, 'B');
이제 한가지 조건이 더 추가되었습니다. "백엔드개발자 중에... 프런트 되는 개발자 물어봤었지? 혹시 디자인 되는 개발자도 있니?" 아까는 백엔드와 프런트에서만 물어봤는데 이제 디자인까지 물어봅니다. 이럴 때는 다시 한번 left join을 사용하면 됩니다.
select * from backend
left join front on backend.name = front.name
left join design on backend.name = design.name;
1 A 1 A null null
4 D 4 D null null
2 B null null 3 B
3 C null null null null
우리는 아까 만들어둔 쿼리에 design 테이블을 다시 left join 함으로써, 추가적인 정보를 얻습니다. "B가 백엔드와 디자인이 가능한 개발자라는 사실을 발견했습니다~!". "아쉽게도 백엔드와 프런트와 디자인까지 되는 개발자는 없습니다." 기억할 것은 우리가 left join을 계속 활용하는데 기준 테이블인 backend는 모두 정상적으로 원래의 row를 유지했다는 사실입니다. 조건이 추가될 때마다 자석처럼 backend 테이블에 데이터가 붙습니다. 여기서 공통집합에 색칠을 넣느냐 빼느냐는 where을 활용하면 됩니다!!!
*정리
join에서 on과 where는 어떻게 사용 할 것인가?
on: 공통 집합 정하기
where : 색칠 영역 정하기
*느낀점
간단한 내용이지만 관련 DB 수정이 필요했기에, 개념을 말로 표현하고 복습하고자 포스팅합니다. 잘 알고 있다고 생각하는 나의 "착각"은 아니었는지 되돌아봅니다. 기준 테이블을 잘 설정해야겠다고 다시 깨달았습니다. join에서 on과 where는 어떤 차이가 있는지 직접 실행해보니 훨씬 잘 이해가 되었습니다. 또한 항상 각 기능의 공통점을 잡아내고 차이점을 확인하면서 더 깊은 이해를 위해 노력했습니다. 추가적으로 사진과 비유와 예시를 사용했습니다. 또한 질문을 만들고 스스로 복습하는 것만큼 인출에 좋은 것은 없습니다.
'학습 > DB' 카테고리의 다른 글
Spring Data Repository Interface (0) | 2021.08.29 |
---|---|
SQL select 쿼리 문법 순서 (0) | 2021.08.28 |
CDATA란? (0) | 2021.08.27 |
oracle ROWNUM & ROW_NUMBER (0) | 2021.08.27 |
WHERE절에 조건(IF문) 추가하기 (0) | 2021.05.08 |