8.1. JOIN이란?8.1.1. PK 와 FK8.1.2. JOIN의 유형8.1.3. JOIN 문법8.2. JOIN 연산8.2.1. INNER JOIN8.2.2. LEFT JOIN8.2.3. RIGHT JOIN8.2.4. FULL OUTER JOIN8.2.5. CROSS JOIN8.2.6. SELF JOIN
8.1. JOIN이란?
조인을 설명하기 앞서 RDBMS(Relational Database Management System) 테이블의 특징을 살펴보겠습니다. RDMS에서 데이터는 테이블 형태로 저장됩니다. 테이블들은 목적과 구조를 가지고 있어, 데이터의 중복성을 줄이고 효율적인 관리를 위해 분리하여 저장합니다.
데이터 정규화
- 데이터 정규화는 데이터의 중복을 제거하고 무결성을 확보하기 위한 과정입니다.
- 데이터베이스 설계 시, 데이터의 중복을 최소화하고, 데이터의 무결성과 일관성을 유지하기 위해 여러 테이블로 데이터를 분리합니다.
- 예를 들면, 고객 정보와 주문 정보를 별도의 테이블로 분리하여 관리할 수 있습니다.
데이터의 효율적 관리
- 각 테이블은 그 테이블만의 특정한 주제나 목적에 맞게 데이터를 저장합니다.
- 데이터의 삽입, 수정, 삭제 등의 작업이 테이블 별로 독립적으로 이루어지므로 데이터 관리의 효율이 증가합니다.
데이터 보안 강화
- 테이블 별로 접근 권한을 설정할 수 있어, 민감한 데이터를 보호할 수 있습니다.
- 급여나 개인 정보 같은 민감 데이터를 포함하는 테이블의 접근을 제한할 수 있습니다.
데이터를 여러 테이블에 분리하여 저장하는 것은 중복성을 줄이고 효율성을 높이는 데 큰 도움이 되지만, 때로는 이러한 분리된 테이블들의 데이터를 함께 조회해야 할 때가 있습니다. 이때 JOIN 연산이 중요하게 작용합니다.
JOIN
은 SQL의 핵심 기능 중 하나로, 두 개 이상의 테이블에서 관련된 데이터를 결합하여 하나의 결과로 반환하는 연산입니다. 이를 통해 분리된 테이블 간의 관계를 활용하여 원하는 데이터를 추출할 수 있습니다. 예를 들면, 고객 정보가 저장된 테이블과 주문 정보가 저장된 테이블을 JOIN 하여, 특정 고객의 주문 내역을 한 번의 쿼리로 조회할 수 있습니다.8.1.1. PK 와 FK
SQL에서 JOIN 연산을 수행할 때, 테이블들 사이에 연결고리 역할을 하는 주요 요소는 "Key(키)"입니다. 이러한 키는 Primary Key(기본 키)와 Foreign Key(외래 키) 두 가지 유형으로 나뉩니다. 해당 키들을 통해 테이블 간의 관계를 정의하고, JOIN 연산을 통해 데이터를 효과적으로 조회하는 할 수 있습니다.
Primary Key(PK)
- Primary Key는 테이블 내에서 각 레코드를 고유하게 구분하는 역할을 합니다.
- 중복된 값을 허용하지 않으며,
NULL
값을 가질 수 없습니다.
- 하나의 테이블 내에는 오직 하나의 Primary Key만 존재할 수 있습니다.
Foreign Key(FK)
- Foreign Key는 다른 테이블의 Primary Key를 참조하는 키로, 관계형 데이터베이스에서 두 테이블 간의 관계를 나타내는 중요한 요소입니다.
- Foreign Key를 통해 참조 무결성(Referential Integrity)을 보장합니다. 이는 Foreign Key가 참조하는 다른 테이블의 Primary Key 값과 일치해야 함을 의미합니다.
- 하나의 테이블은 여러 Foreign Key를 가질 수 있으며, 각각 다른 테이블을 참조할 수 있습니다.
JOIN 연산을 진행할 때, FK와 PK 간의 관계를 기반으로 데이터를 결합합니다. 예시로, HR 데이터 셋을 살펴보면 “employees” 테이블에는 사원에 대한 정보가, “departments” 테이블에는 부서에 대한 정보가 저장되어 있습니다. “employees” 테이블의 FK를 사용하여 “departments” 테이블의 PK를 참조하면, 각 사원이 어떤 부서에 속해 있는지 알 수 있게 됩니다. 이렇게 JOIN 연산을 통해 두 테이블의 관련 정보를 함께 조회하게 되는 것입니다.
.png?table=block&id=fe554bee-7837-40ad-b04b-362bc63992c7&cache=v2)
8.1.2. JOIN의 유형
BigQuery는 다양한 JOIN 유형을 지원합니다.
1. INNER JOIN
- 이는 가장 기본적인 JOIN 유형으로, 두 테이블 간에 일치하는 레코드만 반환합니다.
- 조건에 일치하지 않는 레코드는 결과에서 제외됩니다.
2. LEFT OUTER JOIN (또는 LEFT JOIN)
- 왼쪽 테이블의 모든 레코드와 오른쪽 테이블에서 조건에 일치하는 레코드를 반환합니다.
- 오른쪽 테이블에 일치하는 레코드가 없는 경우, 해당 필드는
NULL
로 표시됩니다.
3. RIGHT OUTER JOIN (또는 RIGHT JOIN)
- 오른쪽 테이블의 모든 레코드와 왼쪽 테이블에서 조건에 일치하는 레코드를 반환합니다.
- 왼쪽 테이블에 일치하는 레코드가 없는 경우, 해당 필드는
NULL
로 표시됩니다.
4. FULL OUTER JOIN (또는 FULL JOIN)
- 두 테이블의 모든 레코드를 반환합니다.
- 두 테이블 중 어느 한쪽에만 존재하는 레코드는 다른 테이블의 필드 값이
NULL
로 표시됩니다.
5. CROSS JOIN
- 두 테이블의 모든 가능한 조합을 반환합니다.
- 이 JOIN 유형은 특별한 경우에만 사용되며, 두 테이블 간에 일치 조건 없이 모든 조합의 결과를 원할 때 사용됩니다.
6. SELF JOIN
- 테이블을 자기 자신과 JOIN 하는 방법입니다.
- 같은 테이블 내에서 레코드 간의 관계를 찾을 때 유용합니다. 예를 들어, 직원과 상사이 모두 같은 테이블에 있을 경우, SELF JOIN을 사용하여 상사-직원 관계를 찾을 수 있습니다.
8.1.3. JOIN 문법
JOIN 연산은 ANSI JOIN과 Oracle JOIN, 두 종류의 SQL 문법이 있습니다. 아래에서 각 JOIN 문법의 설명 및 주요 차이점을 자세히 살펴보겠습니다.
ANSI JOIN
American National Standards Institute (ANSI)에 의해 정의된 표준 SQL JOIN입니다.
- 특징: JOIN 키워드와 함께 조인 조건을 명시적으로 ON 절에서 제공합니다.
- 구문:
# INNER JOIN SELECT * FROM Table1 INNER JOIN Table2 ON Table1.column = Table2.column;
ORACLE JOIN
ORACLE JOIN은 Oracle 데이터베이스에서 전통적으로 사용되던 JOIN 방식입니다.
- 특징: WHERE 절을 사용하여 두 테이블 간의 조인 조건을 제공합니다.
- 구문:
# INNER JOIN SELECT * FROM Table1 , Table2 WHERE Table1.column = Table2.column;
주요 차이점
- 구문: ANSI JOIN은
JOIN
키워드와ON
절을 사용하며, ORACLE JOIN은,
(콤마)와WHERE
절을 사용합니다.
- 표준성: ANSI JOIN은 ANSI SQL 표준에 따른 것이므로 다양한 RDBMS에서 호환성을 가집니다. 반면, ORACLE JOIN은 Oracle 데이터베이스에 특화된 방식입니다.
결론적으로, ANSI JOIN은 표준 SQL에 따른 조인 방식을 나타내며, ORACLE JOIN은 Oracle 데이터베이스의 전통적인 조인 방식을 나타냅니다. 하지만 최신 버전의 Oracle 데이터베이스에서도 ANSI JOIN 구문을 지원하므로, 표준화와 호환성을 위해 ANSI 방식을 사용하는 것을 권장합니다.
8.2. JOIN 연산
8.2.1. INNER JOIN
INNER JOIN은 두 테이블 간에 지정된 조건에 맞는 데이터만 반환하는 JOIN 방식입니다. 즉, 두 테이블에서 지정된 조건에 따라 일치하는 값이 있는 행만 결합하여 반환합니다. 일치하는 값이 없는 행은 결과에서 제외됩니다. 불필요한 데이터를 제외하고, 관련된 데이터만 조회할 때 효율적이며, 결과 집합의 크기를 최소화하여 처리 성능을 향상시킬 수 있습니다.
# ANSI SQL JOIN SELECT * FROM Table1 [INNER] JOIN Table2 ON Table1.column = Table2.column; # ORACLE SQL JOIN SELECT * FROM Table1 , Table2 WHERE Table1.column = Table2.column;
ANSI 문법의 JOIN은 표준 SQL JOIN 구문을 사용하며, Oracle에서 전통적으로 사용되는 JOIN 구문은 WHERE 절을 사용하여 두 테이블을 결합합니다.
ANSI INNER JOIN:
JOIN 조건이 명시적으로
INNER JOIN … ON
부분에 표시되므로 쿼리의 가독성이 좋습니다. 이로 인해 JOIN 조건과 WHERE 절의 필터 조건을 명확하게 구분할 수 있습니다.ORACLE INNER JOIN:
전통적인 Oracle JOIN 방식은
WHERE
절 안에 JOIN 조건을 포함하므로, JOIN 조건과 다른 필터링 조건을 구분하기가 더 어려울 수 있습니다.이해를 돕기 위해 HR 데이터 셋내에 있는 “employees” 테이블과 “departments” 테이블을 INNER JOIN을 활용하여 결합해 보도록 하겠습니다. 두 테이블 모두 “department_id”라는 공통된 컬럼이 있습니다. employees 테이블에서는 “department_id” 컬럼이 FK이며, department 테이블에서는 “department_id” 컬럼이 당연히 PK 역할을 수행합니다.

각 테이블의 “department_id” 값이 같은 행끼리 합쳐져서 하나의 테이블을 반환합니다. 하지만 주의해야 할 점으로는 두 테이블 모두 “department_id” 컬럼이 존재하기 때문에
SELECT
에서 어느 테이블의 컬럼을 가져올 것인지 반드시 명시해야 합니다.또한, 각 테이블에 별칭을 활용하면 복잡한 테이블 이름을 간단한 몇 글자로 줄일 수 있어(
ON
절에서 별칭 사용 가능), 전체적인 쿼리의 길이와 복잡성을 줄일 수 있습니다. 
아래의 예시는 “employees” 테이블과 “departments” 테이블을 결합하여 사원 번호, 사원 이름, 부서 번호 그리고 부서 이름을 조회한 결과입니다.
# ANSI SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e JOIN `HR.departments` AS d ON e.department_id = d.department_id; # ORACLE SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e , `HR.departments` AS d WHERE e.department_id = d.department_id;

추가적인 예시로 INNER JOIN 시 주의할 점을 알아보도록 하겠습니다. “employees” 테이블과 “departments” 테이블을 INNER JOIN하고 사원 번호가 170 보다 큰 경우를 조회했습니다. 해당 결과를 보면 사원 번호 178번의 레코드가 조회되지 않음을 확인할 수 있습니다.
# ANSI SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e JOIN `HR.departments` AS d ON e.department_id = d.department_id WHERE e.employee_id > 170; # ORACLE SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e , `HR.departments` AS d WHERE e.department_id = d.department_id AND e.employee_id > 170;

HR 데이터 셋의 “employees” 테이블의 사원번호가 178인 레코드를 보면 “department_id” 가
NULL
입니다. JOIN 조건인 JOIN ... ON
e.department_id = d.department_id
을 만족하지 않기 때문에, 위 INNER JOIN의 결과에서 사원 번호가 178번인 행은 포함되지 않았습니다.SELECT employee_id , CONCAT(first_name, last_name) AS name , department_id FROM `HR.employees` WHERE employee_id = 178;

이제 사원의 부서와 해당 부서의 주소 정보를 INNER JOIN을 통해 알아보려 합니다. 우선, “employees” 테이블에서 "departments_id"를 사용해 “departments” 테이블을 참조하여 부서명을 가져올 수 있습니다. 상세 주소와 도시 정보는 “departments” 테이블의 "location_id"를 사용하여 결합한 “locations” 테이블에서 가져올 수 있습니다. 마지막으로, “locations” 테이블 내의 "country_id"를 통해 “countries” 테이블과 결합하여 국가 정보까지 파악할 수 있습니다.
# ANSI SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_name , l.street_address , l.city , l.state_province , c.country_name FROM `HR.employees` AS e JOIN `HR.departments` AS d ON e.department_id = d.department_id JOIN `HR.locations` AS l ON d.location_id = l.location_id JOIN `HR.countries` AS c ON l.country_id = c.country_id; # ORACLE SQL JOIN SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_name , l.street_address , l.city , l.state_province , c.country_name FROM `HR.employees` AS e , `HR.departments` AS d , `HR.locations` AS l , `HR.countries` AS c WHERE e.department_id = d.department_id AND d.location_id = l.location_id AND l.country_id = c.country_id ORDER BY 1;

8.2.2. LEFT JOIN
LEFT JOIN, 또는 LEFT OUTER JOIN은 왼쪽 테이블의 모든 레코드와 오른쪽 테이블에서 일치하는 레코드가 반환됩니다. 일치하는 오른쪽 테이블의 레코드가 없는 경우, 결과에는
NULL
값이 포함됩니다.LEFT JOIN은 주로 왼쪽 테이블의 모든 레코드를 포함한 결과를 원할 때 사용됩니다. 예를 들어, 모든 제품과 해당 제품에 대한 주문(있는 경우)을 보여주고 싶을 때 LEFT JOIN을 사용할 수 있습니다.

LEFT JOIN 문법은
LEFT JOIN
또는 LEFT OUTER JOIN
키워드를 사용하여 테이블 간의 관계를 명시적으로 표현합니다.# ANSI SQL JOIN SELECT * FROM Table1 LEFT [OUTER] JOIN Table2 ON Table1.column = Table2.column;
HR 데이터 셋의 테이블들을 LEFT JOIN을 활용하여 결합해 보도록 하겠습니다. 아래의 예시는 “employees” 테이블과 “departments” 테이블을 결합하여 사원 번호, 사원 이름, 부서 번호 그리고 부서 이름을 조회한 결과입니다.
SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e LEFT JOIN `HR.departments` AS d ON e.department_id = d.department_id;

8.2.1. 절에서 INNER JOIN으로 두 테이블을 결합했을 때, 사원 번호 178번의 레코드가 조회되지 않았음을 확인했습니다. 기존의 “employees” 테이블에서 사원번호 178번의 레코드는 “department_id”가
NULL
값이기 때문에 “employees” 테이블과 “departments” 테이블을 “department_id”로 연결 시 조인 조건을 만족하지 않아 결과에 포함되지 않았습니다. SELECT employee_id , CONCAT(first_name, last_name) AS name , department_id FROM `HR.employees` WHERE employee_id = 178;

하지만 LEFT JOIN을 사용하면 왼쪽 테이블의 특정 레코드에 대해 오른쪽 테이블에서 일치하는 레코드가 없을 때, 해당 레코드는 결과 여전히 포함되며 LEFT JOIN 된 오른쪽 테이블의 모든 컬럼 값은
NULL
로 표시됩니다. 이러한 방식으로 LEFT JOIN은 누락된 데이터나 일치하지 않는 레코드를 찾는 데 유용합니다. 결과에서 NULL
값을 확인하여 어떤 레코드가 오른쪽 테이블에 없는지 식별할 수 있습니다.SELECT e.employee_id , CONCAT(e.first_name, e.last_name) AS name , d.department_id , d.department_name FROM `HR.employees` AS e LEFT JOIN `HR.departments` AS d ON e.department_id = d.department_id WHERE e.employee_id > 170;

8.2.3. RIGHT JOIN
RIGHT JOIN, 또는 RIGHT OUTER JOIN은 오른쪽 테이블의 모든 레코드와 왼쪽 테이블에서 일치하는 레코드를 반환합니다. 일치하는 왼쪽 테이블의 레코드가 없는 경우, 결과에는
NULL
값이 포함됩니다.LEFT JOIN 과는 반대로 RIGHT JOIN은 오른쪽 테이블의 모든 레코드를 포함한 결과를 원할 때 사용됩니다.

# ANSI SQL JOIN SELECT * FROM Table1 RIGHT [OUTER] JOIN Table2 ON Table1.column = Table2.column;
RIGHT JOIN을 활용하면 오른쪽 테이블의 특정 레코드가 왼쪽 테이블에서 일치하는 항목을 갖지 않을 경우에도 그 레코드는 반환 결과에 포함됩니다. 이때 RIGHT JOIN 된 왼쪽 테이블의 관련 컬럼 값들은
NULL
로 나타납니다.HR 데이터 셋의 “employees” 테이블과 “department” 테이블을 사용하여 RIGHT JOIN 작업을 진행해 보겠습니다. 해당 예시에서도 “employees” 테이블의 FK인 “department_id”와 “departments” 테이블의 PK인 “department_id”를 이용하여 두 테이블을 결합합니다. 이때, “employees” 테이블은 왼쪽, “departments” 테이블은 오른쪽 위치에 두고 RIGHT JOIN 연산을 수행해 보겠습니다.
SELECT e.employee_id , e.department_id AS e_department_id , d.department_id AS d_department_id , d.department_name FROM `HR.employees` AS e RIGHT JOIN `HR.departments` AS d ON e.department_id = d.department_id ORDER BY e.department_id;

결과 테이블의 “d_department_id” 컬럼의 1행부터 16행까지의 값들은 왼쪽에 위치한 “employees” 테이블의 “department_id”에는 존재하지 않는 값입니다. 따라서 departments 테이블의 모든 항목들은 조회되지만, RIGHT JOIN 된 “employees” 테이블의 “employee_id”와 “e_department_id”는
NULL
값이 반환된 것을 확인할 수 있습니다.8.2.4. FULL OUTER JOIN
FULL OUTER JOIN은 두 테이블 중 하나라도 일치하는 모든 행을 포함하는 결과 집합을 반환합니다. 두 테이블에서 일치하지 않는 열은
NULL
값으로 채워집니다.앞서 설명한 LEFT JOIN과 RIGHT JOIN을 합친 것과 같은 결과를 반환합니다.

# ANSI SQL JOIN SELECT * FROM Table1 FULL [OUTER] JOIN Table2 ON Table1.column = Table2.column;
ANSI SQL JOIN 문법에서 FULL OUTER JOIN 은 OUTER를 생략하고 FULL JOIN으로 사용할 수 있습니다.
“HR” 데이터셋을 사용해서 실습해 보겠습니다. “employees” 테이블과 “departments” 테이블을 FULL OUTER JOIN 해서 모든 부서와 모든 직원을 조회하겠습니다. 먼저, “employees” 데이터를 살펴보겠습니다.
SELECT employee_id , first_name , last_name , department_id FROM `HR.employees` WHERE department_id IS NULL;

“employees” 테이블 중 부서 ID (”department_id”) 정보가 없는 데이터 1개 행이 있습니다.
JOIN 연산을 수행하기 전에 “employees”테이블의 고유한 “department_id”와 “departments”테이블의 고유한 “department_id”를 비교해 보겠습니다. “departments”테이블의 “department_id” 중 “employees”테이블의 “department_id” 와 일치하지 않는 값이 있는 것을 확인할 수 있습니다.
# 왼쪽 결과 테이블 SELECT DISTINCT department_id FROM `HR.employees` ORDER BY 1; # 오른쪽 결과 테이블 SELECT DISTINCT department_id FROM `HR.departments` ORDER BY 1;


“departments”테이블의 PK인 “department_id”와 “employees”테이블의 FK인 “department_id”를 기준으로 FULL OUTER JOIN 하면 아래와 같은 테이블을 반환합니다.
# ANSI SQL JOIN SELECT e.employee_id , e.first_name , e.last_name , d.department_id FROM `HR.employees` AS e FULL JOIN `HR.departments` AS d ON e.department_id = d.department_id;

데이터를 살펴보면서 확인했던 것과 같이 “employees”테이블에서 “department_id”가
NULL
인 행이 결과 집합에 포함되었고, “employees”테이블의 “department_id”와 일치하지 않는 “departments”테이블의 “department_id”행은 “employee_id”, “first_name”, “last_name”열의 값이 NULL
값으로 채워졌습니다.앞서 FULL OUTER JOIN 한 결과 테이블을 LEFT JOIN 과 RIGHT JOIN을 합친 결과 테이블과 비교해 보겠습니다.
(SELECT e.employee_id , e.first_name , e.last_name , d.department_id FROM `HR.employees` AS e LEFT JOIN `HR.departments` AS d ON e.department_id = d.department_id) UNION DISTINCT (SELECT e.employee_id , e.first_name , e.last_name , d.department_id FROM `HR.employees` AS e RIGHT JOIN `HR.departments` AS d ON e.department_id = d.department_id);

UNION 연산으로 인해 두 테이블이 다르게 정렬되어 있지만, 결과 테이블 행의 수를 보면 같은 결과 테이블을 반환한 것을 확인할 수 있습니다.
Oracle SQL JOIN 문법은 FULL OUTER JOIN을 지원하지 않습니다. 또한, MySQL DB는 ANSI JOIN 문법인 FULL OUTER JOIN을 지원하지 않습니다. 따라서, LEFT JOIN과 RIGHT JOIN 결과를 UNION DISTINCT 연산하여 구현합니다.
8.2.5. CROSS JOIN
CROSS JOIN은 Cartesian Product라고도 하며, 두 개의 테이블을 결합할 때 모든 행의 조합을 반환하는 JOIN 연산입니다.

CROSS JOIN은 각 행이 다른 모든 행과 결합됩니다. JOIN을 수행하는 두 테이블 행의 수를 각각 m과 n이라고 했을 때, 출력되는 결과 행의 수는 m x n과 같습니다. CROSS JOIN은 다른 JOIN 연산과 다르게 JOIN 조건을 입력하지 않습니다.
# ANSI SQL JOIN SELECT * FROM Table1 CROSS JOIN Table2; # ORACLE SQL JOIN SELECT * FROM Table1, Table2;
CROSS JOIN은 결과 집합이 크고 중복 데이터가 포함될 수 있으므로 주의해서 사용해야 합니다.
WHERE
절에서 필요한 데이터를 추출해서 JOIN 연산량을 줄일 수 있습니다.“HR” 데이터셋의 “locations”테이블과 “jobs”테이블을 CROSS JOIN 하여 모든 지역의 모든 직업에 대한 조합을 출력해 보겠습니다. 먼저, “locations”테이블과 “jobs”테이블을 살펴보겠습니다. “locations”테이블은 23개의 행, “jobs”테이블은 19개의 행을 갖고 있습니다.
# 왼쪽 결과 테이블 SELECT location_id , street_address , city FROM `HR.locations`; # 오른쪽 결과 테이블 SELECT job_id , job_title FROM `HR.jobs`;


“locations”테이블과 “jobs”테이블을 CROSS JOIN하면 23 x 19 = 437개 행을 가진 집합을 반환합니다.
# ANSI SQL JOIN SELECT l.city , j.job_title FROM `HR.locations` AS l CROSS JOIN `HR.jobs` AS j; # ORACLE SQL JOIN SELECT l.city , j.job_title FROM `HR.locations` AS l, `HR.jobs` AS j;

위의 예시와 같이 CROSS JOIN은 두 테이블 사이의 모든 행의 조합을 출력하기 때문에 다른 JOIN에 비해 결과 집합이 매우 크기 때문에 주의해서 사용해야 합니다.
다음으로 “jobs”테이블의 “max_salary”컬럼을 사용하여 최대 급여가 10,000 이상인 직업과 모든 지역에 대한 조합을 구해 보겠습니다. “max_salary”가 10,000 이상인 직업은 10개인 것을 확인할 수 있습니다.
SELECT COUNT(job_title) FROM `HR.jobs` WHERE max_salary >= 10000;

추가로 “countries”테이블의 국가명을 함께 조회하고, 국가명으로 내림차순 정렬했습니다.
# ANSI SQL JOIN SELECT c.country_name , l.city , j.job_title FROM `HR.locations` AS l JOIN `HR.countries` AS c ON l.country_id = c.country_id CROSS JOIN `HR.jobs` AS j WHERE j.max_salary >= 10000 ORDER BY c.country_name DESC; # ORACLE SQL JOIN SELECT c.country_name , l.city , j.job_title FROM `HR.locations` AS l, `HR.countries` AS c, `HR.jobs` AS j WHERE l.country_id = c.country_id AND j.max_salary >= 10000 ORDER BY c.country_name DESC;

“max_salary”가 10,000 이상인 “jobs”테이블은 10개의 행을 갖고, “locations”테이블과 “countries”테이블을 INNER JOIN 한 테이블은 23개의 행을 가지므로 10 x 23 = 230개의 행을 가진 테이블을 반환합니다. 이와 같이
WHERE
절에서 필요한 데이터를 추출하여 JOIN 연산 크기를 줄일 수 있습니다.8.2.6. SELF JOIN
SELF JOIN은 동일한 테이블 사이의 JOIN 연산입니다. 따라서 다른 JOIN 연산과 달리
FROM
절에 동일한 테이블이 2번 이상 사용됩니다. 일반적으로 SELF JOIN은 테이블 내 다른 행과의 관계를 찾거나 계층 구조 데이터를 처리하기 위해 사용합니다. SELF JOIN은 동일한 테이블을 여러 번 참조해야 하기 때문에 반드시 테이블 별칭(ALIAS)를 사용해야 합니다.# ANSI SQL JOIN SELECT * FROM Table1 AS t1 [INNER|LEFT|RIGHT] JOIN Table2 AS t2 ON t1.column1 = t2.column2;
“HR” 데이터셋의 “employees”테이블을 사용하여 SELF JOIN을 실습해 보겠습니다. 먼저, “employees”테이블을 살펴보겠습니다.
SELECT employee_id , first_name , email , department_id , manager_id FROM `HR.employees`;

“employees”테이블은 107명의 직원에 대한 정보를 갖는 테이블입니다. 이 테이블은 각 직원을 관리하는 상사의 “employee_id”(”manager_id”)를 갖습니다. SELF JOIN을 사용하여 직원 정보와 함께 관리자명을 반환해 보겠습니다. 최상위 관리자의 경우, “manager_id”열이
NULL
값을 갖기 때문에 LEFT JOIN을 사용하겠습니다.# ANSI SQL JOIN SELECT e.employee_id , e.first_name , e.email , e.department_id , m.employee_id AS manager_id , m.first_name AS manager_name FROM `HR.employees` AS e LEFT JOIN `HR.employees` AS m ON e.manager_id = m.employee_id;

SELF JOIN은 데이터베이스에서 복잡한 관계와 계층 구조를 다룰 때 유용합니다. 특히 조직 구조, 제품 범주, 계층적 데이터 구조와 관련된 데이터를 처리하기 위해 사용합니다.