본문 바로가기
백엔드/데이터베이스

데이터베이스(INNER JOIN)

by study_yeon 2023. 8. 1.

2023.07.13 ~07.17 수업  (script 22)

 

◎ JOIN이란

- 한 데이터베이스 내의 여러 테이블의 레코드를 조합하여 하나의 열로 표현한 것
- 두 개의 테이블을 서로 묶어서 하나의 결과를 만들어 내는 것

- 중복된 데이터가 많이 들어 있는 테이블은 관리가 어려움
- 불필요한 중복된 데이터에 의해 하드디스크(저장공간)의 낭비가 심하고 불필요한 정보로 인한 데이터의 오류가 발생할 수 있다(무결관, 일관성 등)
- 관계형데이터베이스에서 중복된 테이블의 데이터를 별도의 테이블로 분리하고 
각각의 테이블을 연결하는 유일키를 사용하여 필요할때 테이블끼리 연결하여 확장하며 
중복데이터 문제를 해결하는 것을 조인이라고 한다.
- 더 많은 테이블도 각각 두 테이블간의 조인들의 합이기도 함

 

(참고) 조인은 수평방향, 유니온은 수직방향으로 확장됨

[사용조건]
- 테이블 두개 이상
- 테이블을 조인하려면 각 테이블을 연결하기(관계 설정)위해 사용할 컬럼이 필요
- 이 컬럼들이 바로 두 테이블의 연결고리 역할을 하는 컬럼이다
- 두 개의 연결고리 컬럼은 각 테이블에 하나씩 존재
- 연결고리컬럼을 이용하여 SELECT문의 WHERE 조건절에서 연결을 위한 각종 조건연산을 사용해서 조인을 수행
- 연결고리컬럼은 보통 같은 이름과 같은 데이터형을 사용하고 한쪽에서는 중요한 키(기본키, primary key)로 다른쪽에서는 참조키(외래키, forgine)로 사용한다

 

INNER JOIN : 내부 조인은 둘 이상의 테이블에 존재하는 공통 속성의 값이 같은 것을 결과로 추출
-> 두 테이블을 조인할 때, 두 테이블에 모두 지정한 열의 데이터가 있어야 한다.

1) 동등 조인(EQUAL JOIN) : 둘 이상의 테이블에 존재하는 공통 속성의 동등 비교만을 사용. 부등호 조인은 동등조인에 포함하지 않는다.

2) 자연 조인(NATURAL JOIN) : 조인 대상 테이블의 모든 컬럼들을 비교하여, 같은 컬럼명을 가진 대상으로 조인을 수행. 그리고, 중복된 컬럼은 한번만 출력

OUTER JOIN : 조인하는 여러테이블에서 한 쪽에는 데이터가 있고, 한 쪽에는 데이터가 없는 경우, 데이터가 있는 쪽 테이블의 내용을 모두 출력하는 것
-> 건에 맞지 않아도 해당하는 행을 출력하고 싶을 때 사용

1) LEFT OUTER JOIN: 무조건 왼쪽 테이블의 값이 모두 출력
- 왼쪽에 조인 컬럼 값이 없는 경우(NULL)를 포함
- 오른쪽 테이블(두 번째 테이블)의 조인 컬럼에 데이터가 없어서 왼쪽 테이블의 조인 컬럼에 NULL값이 추가되는 레코드를 포함하여 가져와라

2) RIGHT OUTER JOIN : 무조건 오른쪽 테이블의 값이 모두 출력
- 오른쪽에 조인 컬럼 값이 없는 경우(NULL)를 포함
3) (UNION)FULL OUTER JOIN : 두  가지 경우를 합함

 

* SQL편집기 이름 변경하는 법
Ctrl + f2


▶ 내부조인(INNER JOIN) 

 

※ JOIN으로 관리해야하는 이유
- 데이터가 너무 많다면 테이블을 분리해주어야함

자료가 많다면
테이블 분리가 필요

- 중복데이터인 부서테이블, 주소테이블 분리하여 만들기
- 부서번호 또는 주소번호만 작성하면 나머지 생략 가능


▷ 실습용 테이블 만들기 

- 사원, 부서, 주소 테이블 각각 만들기 

-- 1. 사원 테이블
CREATE TABLE tbl_employee (
	emp_id     NUMBER NOT NULL,-- 사원번호
	emp_name   varchar2(100),  -- 사원명
	gender     varchar2(10),   -- 성별
	age        NUMBER,	   -- 나이
	hire_date  DATE,	   -- 입사일
	dept_id    NUMBER,	   -- 외부테이블 부서테이블의 기본키(외래키로 사용)
	address_id NUMBER,	   -- 외부테이블 주소테이블의 기본키(외래키로 사용)
	PRIMARY key(emp_id) 
	-- 기본키의 인덱스 이름을 직접 정하는 방법 : 
	-- CONSTRAINT 기본키파일이름 PRIMARY KEY(기본키 컬럼이름) 사용
	-- 예)CONSTRAINT tbl_employee_pk
);
-- 2. 부서 테이블
CREATE TABLE tbl_dept (
	dept_id   NUMBER NOT NULL,		   -- 부서id
	dept_name varchar2(50),			   -- 부서명
	use_yn    varchar2(2) DEFAULT 'Y',	   -- 사용여부
	dept_desc varchar2(200),		   -- 부서설명
	CONSTRAINT tbl_dept_pk PRIMARY key(dept_id)
);
-- 3. 주소 테이블
CREATE TABLE tbl_address (
	address_id   NUMBER NOT NULL, 	  	-- 주소일련번호
	city 		 varchar2(100),    	 -- 도시명
	gu			 varchar2(50),   -- 구 이름
	address_name varchar2(100),   	 	 -- 나머지 주소
	CONSTRAINT   tbl_address_pk  PRIMARY key(address_id)
);


※ 오라클에서는 테이블과 comment를 같이 사용할 수 없어 별도로 분리하여 작성해야함

1. 오라클에서 객체 주석 사용하기 
- 객체주석을 사용하면 자기설명기능이 추가되어 나중에 컬럼의 용도나 중요한 정보를 추가하여 개발시 참조할 수 있다
예) COMMENT ON COLUMN tbl_dept.use_yn IS '부서가 현재 사용중이면 Y 아니면 N';
-> 주석 설명을 통해 어떤 값이 들어가는지 쉽게 알 수 있다

2) [구문]

COMMENT ON [TABLE or COLUMN] 객체이름 IS '객체설명';
COMMENT ON TABLE 테이블명 IS '주석문';
COMMENT ON COLUMN 테이블명.컬럼객체명 IS '주석문';

오라클은 COMMENT 작성시 별도의 COMMENT ON 객체타입 객체명 IS '설명내용' 형식 사용
예)COMMENT ON TABLE tbl_employee IS '사원테이블';

3) 추가된 코멘트 확인하기
[구문]

SELECT * FROM ALL_COL_COMMENTS
    WHERE TABLE_NAME = '테이블명' -- 테이블이름을 문자열로 주기

▷ 테이블 및 객체 주석(COMMENT) 처리

COMMENT ON TABLE tbl_address IS '주소테이블';
COMMENT ON COLUMN tbl_address.address_id   IS '주소일련번호';
COMMENT ON COLUMN tbl_address.city		   IS '도시명';
COMMENT ON COLUMN tbl_address.gu		   IS '구 이름';
COMMENT ON COLUMN tbl_address.address_name IS '나머지 주소';

-- (테이블 주석 확인)
SELECT *    -- 테이블의 코멘트 확인
	FROM ALL_TAB_COMMENTS
	WHERE table_name = UPPER('tbl_address');

SELECT *    -- 테이블의 코멘트 확인(대문자로 해야 정확히 확인 가능)
	FROM ALL_COL_COMMENTS
	WHERE table_name = UPPER('tbl_address');

▷ 실습에 사용할 데이터 추가(INSERT INTO)

-- 1. tbl_employee
INSERT INTO TBL_EMPLOYEE (
		emp_id, emp_name, gender, age, 
		hire_date, dept_id, address_id )
	VALUES(
		1, '김유신', '남성', 56, 
		TO_DATE('2023-07-01', 'YYYY-MM_DD'), 1, 2);

INSERT INTO TBL_EMPLOYEE (
		emp_id, emp_name, gender, age, 
		hire_date, dept_id, address_id )
	VALUES(
		2, '신사임당', '여성', 34, 
		TO_DATE('2023-07-01', 'YYYY-MM_DD'), 1, 1);
	
INSERT INTO TBL_EMPLOYEE (
		emp_id, emp_name, gender, age, 
		hire_date, dept_id, address_id )
	VALUES(
		3, '홍길동', '남성', 45, 
		TO_DATE('2023-07-01', 'YYYY-MM_DD'), 3, 2);
	
INSERT INTO TBL_EMPLOYEE (
		emp_id, emp_name, gender, age, 
		hire_date, dept_id, address_id )
	VALUES(
		4, '강감찬', '남성', 23, 
		TO_DATE('2023-07-01', 'YYYY-MM_DD'), 2, 3);	
	
INSERT INTO TBL_EMPLOYEE (
		emp_id, emp_name, gender, age, 
		hire_date, dept_id, address_id )
	VALUES(
		5, '세종대왕', '남성', 45, 
		TO_DATE('2023-07-01', 'YYYY-MM_DD'), 4, 4);
-- 2. tbl_dept
INSERT INTO TBL_DEPT (dept_id, dept_name)
	VALUES(1, '회계팀');

INSERT INTO TBL_DEPT (dept_id, dept_name)
	VALUES(2, '경영팀');

INSERT INTO TBL_DEPT (dept_id, dept_name)
	VALUES(3, '전산팀');

INSERT INTO TBL_DEPT (dept_id, dept_name)
	VALUES(4, '마케팅팀');
-- 3. tbl_address
INSERT INTO TBL_ADDRESS (address_id, city, gu, address_name)
	VALUES (
	1, '서울특별시', '중구', '새문안로 12');

INSERT INTO TBL_ADDRESS (address_id, city, gu, address_name)
	VALUES (
	2, '서울특별시', '서대문구', '연희로 15길');

INSERT INTO TBL_ADDRESS (address_id, city, gu, address_name)
	VALUES (
	3, '서울특별시', '영등포구', '여의대로 99');

INSERT INTO TBL_ADDRESS (address_id, city, gu, address_name)
	VALUES (
	4, '서울특별시', '강남구', '테해란로 33');
-- 추가한 데이터 확인
SELECT * FROM TBL_EMPLOYEE;
SELECT count(*) FROM TBL_DEPT;
SELECT count(*) FROM TBL_ADDRESS;

▷ 분리한 테이블을 조인하기

- 사원테이블은 부서테이블, 주소테이블과 연결되어있음
-> 원하는 것은 이 사원의 부서정보를 얻는 것
- 왼쪽 레코드에 대하여 오른쪽 레코드가 1개만 일치하므로 전체레코드(카테시안 곱)에서 inner join을 통해
일치하는 일부레코드들을 출력

 

1) 사원 - 부서 테이블 조인

1-1) 카테시안 곱(cross join) ver

-> 사원테이블의 각 사원레코드가 가능한 모든 부서 보직의 가능성을 전부 출력(말이 안되는 자료)

-- 테이블 두개를 연결하여 사원-부서테이블 만들기
-- 카테시안곱(Cartesian Product) : 컬럼의 결과가 경우의 수 개수 형태로 곱셈이 되어 출력
SELECT 
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_ID, td.DEPT_NAME, td.USE_YN
	FROM 
		TBL_EMPLOYEE te,
		TBL_DEPT td;

-> 내부조인(equal join)으로 해결하기

 

※ 내부조인 (inner join : equal join) 

- WHERE절에서 같음(동등 : =)연산자를 사용하는 조인으로, 연결고리컬럼(=조인컬럼)을 비교하여 테이블에서 데이터를 연결하는 방법
- inner join이란 왼쪽테이블을 기준으로 오른쪽테이블의 레코드 전체에서 일치하는 내용만 보여라(교집합과 유사)
즉, 전체에서 일치하는 부분내용만 보여준다고해서 inner join이라고 한다

- 오라클 조인(Oracle Join)과 안시 조인(ANSI JOIN)을 사용

 

* 오라클
[구문] 
- 앞에 나오는 t1이 기준테이블

SELECT 
    t1.* [또는 t1.컬럼리스트],
    t2.* [또는 t2.컬럼리스트]
    FROM
        table1 t1,
        table2 t2
        WHERE
        t1.외부키컬럼 = t2.기본키컬럼; -- 조인컬럼

1-2) 사원 - 부서 테이블 내부조인 오라클 ver

-- 내부조인으로 해결하기
-- 일치하는 정보만 출력하기	
SELECT 
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_ID, td.DEPT_NAME, td.USE_YN
	FROM 
		TBL_EMPLOYEE te,
		TBL_DEPT td
	WHERE
		-- 사원테이블의 외부키(forgein)는 부서테이블에서 왔으므로 부서코드와 같아야함
		-- 선택된 정보가 사원-부서테이블에 추가할 내용
		te.DEPT_ID = td.DEPT_ID;


* ANSI(기술이나 규격 표준화를 주도하는 국제기구에서 제안한 SQL문법)
[구문] 
- INNER JOIN이라는 키워드절을 사용하여 기준테이블과 관련테이블로 구분한다
- WHERE절은 전체 테이블에 제한을 주는 조건으로 사용하고 두 테이블사이의 연결제한 조건은 ON이라는 연산자 사용

SELECT 
    t1.* [또는 t1.컬럼리스트],
    t2.* [또는 t2.컬럼리스트]
    FROM
        table1 t1 -- 기준테이블
    INNER JOIN
        table2 t2
      ON t1.외부키컬럼 = t2.외부키컬럼
    WHERE 
        전체 제한 조건
    ORDER BY
        정렬조건;

1-3) 사원 - 부서 테이블 내부조인 ANSI ver

-- ANSI표준 사용하여 조인
SELECT 
    te.*,
    ta.*
    FROM
		  TBL_EMPLOYEE te
    	INNER JOIN
    	  TBL_ADDRESS ta
    	  ON te.ADDRESS_ID = ta.ADDRESS_ID
    ORDER BY
    	te.EMP_ID;

2) 사원 - 주소 테이블 조인

2-1) 카테시안 곱 ver

-> 사원테이블의 각 사원레코드가 가능한 모든 주소의 가능성을 전부 출력(말이 안되는 자료)

-- 사원테이블과 주소테이블 합치기(카테시안 곱)
SELECT 
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	ta.ADDRESS_ID, ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te,
	TBL_ADDRESS ta;

2-2) 내부조인 오라클 ver

-- 사원테이블의 주소번호컬럼과 주소테이블의 주소번호컬럼이 같은(ADDRESS_ID) 정보 추출
-- 사원테이블의 사원이 사는 곳 정보 출력하기(inner join)
SELECT 
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	ta.ADDRESS_ID, ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te,
	TBL_ADDRESS ta
WHERE te.ADDRESS_ID = ta.ADDRESS_ID;

2-3) 내부조인 ANSI ver

-- ANSI표준 사용하여 조인
SELECT 
    te.*,
    ta.*
    FROM
		  TBL_EMPLOYEE te
    	INNER JOIN
    	  TBL_ADDRESS ta
    	  ON te.ADDRESS_ID = ta.ADDRESS_ID
    ORDER BY
    	te.EMP_ID;

※ 내부조인 연습하기

 

▷ 사원테이블의 정보 가져오기(부서, 주소테이블과 결합하기)
- 그 결과에서 남성만 조회하기
1) 오라클

SELECT -- ID는 사원테이블만 출력하기
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_NAME, td.USE_YN,
	ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te,
	TBL_DEPT td,
	TBL_ADDRESS ta
WHERE 
	te.DEPT_ID = td.DEPT_ID AND
	te.ADDRESS_ID = ta.ADDRESS_ID AND 
	te.GENDER = '남성';


2) ANSI

SELECT 
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_NAME, td.USE_YN,
	ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te
	 INNER JOIN 
		TBL_DEPT td
		ON te.DEPT_ID = td.DEPT_ID
	 INNER JOIN 
	 	TBL_ADDRESS ta 
	 	ON te.ADDRESS_ID = ta.ADDRESS_ID
	 WHERE 
	 	te.GENDER = '남성';


▷ 사원테이블 기준으로 부서, 주소테이블과 결합하여 나이 40 ~ 50사이 구하기

1) 오라클

SELECT
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_NAME, td.USE_YN,
	ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te,
	TBL_DEPT td,
	TBL_ADDRESS ta
WHERE 	
	te.DEPT_ID = td.DEPT_ID AND 
	te.ADDRESS_ID = ta.ADDRESS_ID AND 
	te.AGE BETWEEN 40 AND 50;

2) ANSI

SELECT
	te.EMP_ID, te.EMP_NAME, te.GENDER, te.AGE, te.HIRE_DATE,
	td.DEPT_NAME, td.USE_YN,
	ta.CITY, ta.GU, ta.ADDRESS_NAME
FROM 
	TBL_EMPLOYEE te
		INNER JOIN
			TBL_DEPT td
			ON te.DEPT_ID = td.DEPT_ID
		INNER JOIN 
			TBL_ADDRESS ta 
			ON te.ADDRESS_ID = ta.ADDRESS_ID
		WHERE 	
			te.AGE BETWEEN 40 AND 50;