여행을 개발하다
[Join] Join의 정의 및 문법 본문
이번에는 데이터베이스의 Join에 대해 포스팅하고자 한다.
나는 실무에 투입되기 전, Join 구문을 배운적도 없었기 때문에 사용경험이 단 1도 없는 상태였다.
당연히 SELECT, UPDATE, DELETE, INSERT만 주구장창 배웠고, 예제로 사용했던 데이터베이스도 많아야 2개의 테이블만 포함되어 있었다. 하지만 처음 투입된 셀트리온 프로젝트에서 본 모든 SQL은 JOIN 파티라고 해도 이상하지 않을만큼 그 사용빈도와 중요도가 높았다.
그럼 JOIN이 무엇이고, 그 종류에는 어떤 것들이 있으며, 어느 상황에서 쓰이는지 알아보도록 한다.
조인(Join)이란?
- 다수의 테이블에 포진해있는 데이터들을 조합하여 하나의 열(ROW)로 가져올(SELECT) 때 사용한다.
- 반드시 테이블들 간 특정 관계(Relation); 연결관계가 있어야 한다.
말로 풀어보려하니 어렵다. Join의 사전적인 의미는 붙다, 어울리다, 합치다의 뜻이 있다.그래서 실제로도 여러 테이블들에 있는 데이터, 즉 레코드를 하나로 합친다는 말 그대로의 역할을 한다.
예를 들어보자.
A라는 데이터베이스가 있다. 데이터베이스 A는 어떤 회사에서 전사적인 인사, 재무, 마케팅, 경영관리에 관련된 데이터들을 저장하는데 사용하려고 한다. 데이터베이스 A의 설계자는 앞으로 수백개의 테이블들을 하나하나 만들어 나갈 예정이다.
어떤 테이블을 가장 먼저 생성할까 고민하던 중, 기초적인 항목들부터 생각해보았다. 회사는 사람이 모이는 곳이고, 사람들이 일을 하며 가치를 창출한다. 설계사는 '아, 직원들의 정보 관리가 제일 중요하다'라고 판단하고 직원들의 정보를 저장할 테이블을 먼저 만들기로 했다. 그리고 이 테이블에서 관리되어야 하는 정보들을 리스트업 해보았다.
사번, 이름, 학력, 전공, 연봉정보, 고용형태(정규직/계약직), 전화번호, 주소...
어우 생각해보니 너무나 많았다. 그래서 일단 위에 나열한 컬럼들로만 구성된 테이블 SAWON을 만들었다.
CREATE TABLE SAWON
( SABUN VARCHAR2(10) NOT NULL PRIMARY KEY,
NAME VARCHAR2(30) NOT NULL,
ACADEMY VARCHAR2(30),
MAJOR VARCHAR2(30),
SALARY NUMBER,
EMP_TYPE VARCHAR2(10),
PHONE VARCHAR2(30),
ADDRESS VARCHAR2(50)
);
이제 레코드를 하나하나 넣어볼까 했는데, 아차 문제가 생겼다. 다름 아닌 직원들의 '학력' 때문이었다. 고등학교 이후의 학력 정보는 모두 관리가 되어야 하는데, 어떤 직원들은 고등학교까지만 졸업했고, 어떤 직원들은 고등학교를 거쳐 대학교까지 나왔고, 심지어 어떤 직원들은 대학원까지 나왔다.
더 난감한 것은 어떤 직원은 고등학교 때는 실업계였지만, 대학교는 영문학 전공으로 졸업했고, 대학원은 컴퓨터공학으로 졸업했다. 그렇다고 SAWON 테이블에 학력1, 전공1, 학력2, 전공2,.. 와 같이 무제한 컬럼을 만들기에는 자원관리에 문제가 있어 보인다.
결국, 데이터베이스의 효율적인 관리를 위해 학력정보만 관리하는 ACADEMY 테이블을 별도로 만들기로 했다. 그리고 기존의 SAWON 테이블에서 관리하던 ACADEMY와 MAJOR를 필드를 삭제하고, ACADEMY 테이블에서 별도로 관리하기로 했다.
ALTER TABLE SAWON DROP COLUMN ACADEMY;
ALTER TABLE SAWON DROP COLUMN MAJOR;
COMMIT;
ACADEMY라는 테이블을 구성하는 컬럼은 다음과 같다.
사번, SEQ, 학교명, 학위명, 전공명, 졸업년도
CREATE TABLE ACADEMY
( SABUN VARCHAR2(10) NOT NULL,
SEQ NUMBER NOT NULL,
SCH_NM VARCHAR2(30),
DEGREE VARCHAR2(30),
MAJOR VARCHAR2(30),
GRAD_YEAR VARCHAR2(6), -- YYYYMM
CONSTRAINT academy_pk2 PRIMARY KEY(SABUN, SEQ)
);
여기서 SEQ는 한 사원의 학력에 대한 시퀀스이다.
가령, '정영훈(사번 : 2020012901)'이라는 사원의 학력을 가고등학교, 나대학교, 다대학원 3개로 가정하면, (2020012901, 1, 가고등학교,...), (2020012901, 2, 나대학교, ...), ... 순으로 INSERT를 한다.
즉, 동일한 사번이라면 SEQ가 1부터 1씩 증가하고, 다른 사번이라면 SEQ가 다시 1부터 1씩 증가하는 형태이다. 결국, ACADEMY 테이블에서 각각의 레코드를 구분할 수 있는 PRIMARY KEY는 (SABUN, SEQ) 한 쌍의 복합키가 된다.
이러한 과정을 거쳐 몇 백개의 테이블로 구성된 A라는 데이터베이스가 생성되었다. 그리고 이제는 데이터베이스 A를 바탕으로 개발자들이 프로그램을 만들기 시작했다.
시스템상에는 매출관리, 경영통계, 인사정보 등의 여러 화면들이 있었지만,사원들의 학력정보 관리 화면은 개발자 '갑'이 맡아 개발하기로 했다.학력정보 관리 페이지에 대한 요구사항은 '학력정보의 추가, 삭제, 수정 및 특정 사원의 이름으로 학력정보를 검색하는 기능'이 있다.
개발자 갑은 ACADEMY 테이블의 데이터들을 가져오는 쿼리를 작성한다.
SELECT * FROM ACADEMY;
그런데 문제가 하나 있다. ACADEMY 테이블의 정보를 가져오는 것은 쉬웠다. 그러나 사번만 보고는 그 사람의 이름이 무엇인지 알수는 없다. 물론, 사용자가 이미 알고 있는 사람의 사번이거나 머리가 비상하여 사번-이름을 매칭시킬 수 있는 능력이 있을수도 있겠다. 하지만 전사 직원은 1만명이나 된다.
데이터베이스를 직접 컨트롤하는 개발자나 DBA라면 사번만 복사해서 SAWON 테이블을 탐색하기만 하면 되지만, 화면 인터페이스만 보고 작업하는 일반 사용자들에게는 분명한 한계점이다. 더구나 특정 사원의 이름으로 학력정보를 검색하는 기능도 이미 요구사항에 있다.
다행스러운 것은 ACADEMY 테이블 자체에도 SABUN이 있고, 이 SABUN에 매칭되는 이름, 주소, 연봉 등의 기초정보들이 SAWON 테이블에 존재한다. 그래서 개발자 갑은 ACADEMY 테이블의 모든 데이터와, 해당 SABUN을 가지고 있는 인력의 이름만 SAWON 테이블에서 별도로 가져와 보여주고자 한다.
이럴 때 사용하는 것이 'JOIN' 구문이다. 스토리성으로 길게 얘기했지만, 위의 JOIN이 무엇인지 정의해놓은 2줄의 내용이 모두 담겨있다.
① 다수의 테이블에 포진해있는 데이터들을 조합하여 하나의 열로 가져올(SELECT) 때 사용한다.
→ ACADEMY의 모든 컬럼과, SAWON의 NAME 컬럼을 1개의 ROW로 SELECT 해온다.
② 반드시 테이블들 간의 특정 관계(Relation); 연결관계가 있어야 한다.
→ SAWON과 ACADEMY 테이블을 서로 다른 컬럼들을 가지고 있지만, 공통적으로 SABUN이라는 하나의 컬럼을 모두 가지고 있다.
Join 방법 및 문법
Join은원본 테이블, 조인 종류, 조인하고자 하는 테이블, 조인 조건 순으로 적어준다.
테이블A (원본테이블)
조인 종류
테이블B (조인하고자 하는 테이블)
ON A.컬럼 = B.컬럼 (조인조건)
이렇게 보니 또 무슨 말인지 모르겠다. 위의 SAWON과 ACAMEMY 테이블 예제를 다시 사용해본다.
개발자는 일단 ACAMEDY의 모든 컬럼을 가져온다.
SELECT * FROM ACAMEMY;
그리고 이제는 SAWON 테이블에서 NAME 컬럼을 가져오고자 한다.
조인 방법은 LEFT OUTER JOIN (추후 포스팅 예정)이고, 테이블 ACADEMY와 조인하고자 하는 테이블은 SAWON(B)이다. 조인 조건은 ACADEMY 테이블의 SABUN과 SAWON 테이블의 SABUN과 같은 레코드인 것들이다.
LEFT OUTER JOIN -- 조인 종류
SAWON -- 테이블 B
ON ACADEMY.SABUN = SAWON.SABUN -- 조인 조건
SELECT *
FROM ACADEMY
LEFT OUTER JOIN
SAWON
ON ACADEMY.SABUN = SAWON.SABUN
이렇게 완성된 SQL은 다음과 같은 서로 다른 테이블 출신인 컬럼들을 하나의 ROW로 가져온다.
SABUN SEQ SCH_NM DEGREE MAJOR GRAD_YEAR SABUN NAME
----------- ----- ---------- --------- -------- --------- --------- -------
2020012901 1 가 고등학교 고등학교 실업계 201002 2020012901 정영훈
2020012901 2 나 대학교 학사 영어영문학 201702 2020012901 정영훈
상황에 따라 필요한 컬럼만을 가져올 수도 있다. 예를 들어, ACADEMY에서는 사번과 학교이름, SAWON 테이블에서는 이름만 가져오고 싶다면 다음과 같이 작성하면 된다.
SELECT ACADEMY.SABUN,
ACADEMY.SCH_NM,
SAWON.NAME
FROM ACADEMY
LEFT OUTER JOIN
SAWON
ON ACADEMY.SABUN = SAWON.SABUN
SABUN SCH_NM NAME
---------- ---------------- ------------
2020012901 가 고등학교 정영훈
2020012901 나 대학교 정영훈
* Alias(알리아스, 별칭) 사용
예제로 든 SAWON, ACADEMY 같이 비교적 간단(?)한 이름을 가진 테이블은 상관이 없지만, 가끔 테이블의 이름이 비정상적으로 길어질 때도 발생할 수 있다(ex. TB_CMM_SAWON_INFO........ 와 같이). 그럴 때는 테이블의 이름에 별칭(별명이라고 생각하면 쉬울 듯)을 붙여 사용할 수도 있는데, 이를 Alias(알리아스)라고 한다.
ACADEMY 테이블의 별칭을 X, SAWON 테이블의 별칭을 Y로 한다고 치자. 그럼 다음과 같이 사용할 수 있다.
SELECT * FROM ACADEMY AS X; SELECT * FROM SAWON AS Y;
SELECT * FROM ACADEMY AS X;
SELECT * FROM SAWON AS Y;
사용법은 쉽다. 'AS + 별칭(Alias)' 형태로 주면 된다. 또한, AS는 생략 가능하다.
SELECT * FROM ACADEMY X;
SELECT * FROM SAWON Y;
당연히 AS를 생략할 때는 원본 테이블명과 띄어서 사용해야 한다.
알리아스는 단순 테이블 뿐만 아니라 컬럼에도 줄 수 있다. 그리고 특정 컬럼을 SELECT 할 때, ALIAS.COULMN과 같이 '.'으로 구분하여 해당 테이블의 컬럼에 접근할 수 있다.
결국, 위의 SQL은 다음과 같이 작성할 수도 있다.
SELECT X.SABUN,
X.SCH_NM,
Y.NAME
FROM ACADEMY X
LEFT OUTER JOIN
SAWON Y
ON X.SABUN = Y.SABUN;
지금까지 JOIN이 무엇인지, 그리고 JOIN의 문법과 사용법에 대해 알아보았다. 다음 포스팅에서는 JOIN의 종류에는 어떤 것들이 있는지 알아본다.
감사합니다!
'데이터베이스 > DB 일반' 카테고리의 다른 글
[Join] ANSI Join & Oracle Join, Join의 종류 (0) | 2020.02.02 |
---|