04 - 1 조인
테이블을 2개 이상 조인한다는 의미
조인은 테이블 A의 열과 테이블 B의 정보를 포함해 검색할 수 있게 만들어 줍니다. 데이터를 중복 저장하면 저장할 공간이 더 필요한 문제와, 중복 저장한 열 데이터를 수정할 경우 해당 열을 가진 테이블의 데이터를 모두 찾아 수정해야 하는 문제가 발생합니다. 결국 데이터 중복을 최소화하는 설계는 피할 수 없으므로, 조인을 사용해 2개 이상의 테이블을 조합한 결과를 검색하는 방식이 효율적일 것입니다.
내부 조인
조인은 상황에 따라 여러 종류로 구분해 사용합니다. 먼저 알아볼 조인은 가장 많이 사용하는 조인인 내부 조인(INNER JOIN)으로, 보통 조인이라고 하면 내부 조인을 가리킬 만큼 널리 쓰이는 형태입니다.
내부 조인은 조인키에 해당하는 각 테이블의 열값을 비교해 조건에 맞는 값을 검색합니다.
-- 내부 조인의 기본 형식
SELECT [열 이름]
FROM [테이블 1]
INNER JOIN [테이블 2] ON [테이블 1.열] = [테이블 2.열]
WHERE [검색 조건]
INNER JOIN 문의 기본 개념 알아보기
INNER JOIN 문 사용하기(2개 테이블 조인)
조인은 2개 이상의 테이블을 전제로 하며, 실제 쿼리를 작성할 때는 SELECT 문에 다른 문을 추가하는 것이 아니라 FROM 문에 조인할 테이블을 나열합니다.
-- INNER JOIN 문으로 2개 테이블 조인 : nasdaq_company, stock
SELECT
a.symbol,
a.company_name,
a.ipo_year,
a.sector,
a.industry,
b.date,
b.[open],
b.[high],
b.[low],
b.[close],
b.adj_close,
b.volume
FROM nasdaq_company AS a
INNER JOIN stock AS b ON a.symbol = b.symbol
WHERE a.symbol = 'MSFT'
AND b.date >= '2021-10-01'
AND b.date < '2021-11-01'
INNER JOIN 문 사용하기(2개 이상의 조건 적용)
조인 조건으로 2개 이상의 열을 사용할 수도 있습니다. 이때 조건은 AND, OR 등을 사용해 여러 조건을 조합할 수도 있습니다.
-- 조건으로 사용한 열값이 같은 데이터를 INNER JOIN으로 검색
SELECT
a.symbol,
a.last_crawel_date,
b.date
FROM nasdaq_company AS a
INNER JOIN stock AS b ON a.symbol = b.symbol AND a.last_crawel_date = b.date
WHERE a.symbol = 'MSFT'
INNER JOIN 문 사용하기(3개 이상의 테이블 조인)
3개 이상의 테이블을 조인할 때는 두 테이블의 관계가 다대다 관계인 경우가 많습니다.
-- 3개 이상 테이블의 INNER JOIN 기본 형식
SELECT [열 이름]
FROM [테이블 1]
INNER JOIN [테이블 2] ON [테이블 1.열] = [테이블 2.열]
INNER JOIN [테이블 3] ON [테이블 2.열] = [테이블 3.열]
WHERE [검색 조건]
-- 3개 이상의 테이블을 INNER JOIN으로 검색
SELECT
a.industry,
c.symbol,
c.company_name,
c.ipo_year,
c.sector
FROM industry_group AS a
INNER JOIN industry_group_symbol AS b ON a.num = b.num
INNER JOIN nasdaq_company AS c ON b.symbol = c.symbol
WHERE a.industry = N'자동차'
ORDER BY symbol
INNER JOIN은 조건에 맞는 데이터만 검색하므로 NULL 데이터가 발생하지 않습니다. 따라서 열이 중복된 상태에서 고려할 부분은 '성능'입니다. 성능을 높이려면 인덱스가 형성된 열을 우선 사용하는 것이 좋습니다. 또는 데이터 양이 적은 열을 조인 조건으로 우선 사용하는 것이 좋습니다.
외부 조인
다른 테이블에 있는 행에서 일치 항목이 아닌 행을 조합해 검색해야 할 경우 외부 조인(OUTER JOIN)을 사용합니다.외부 조인은 열의 일치 항목을 고려하지 않고, 한쪽 테이블을 다른 쪽 테이블에 조합할 때 사용합니다.
-- 외부 조인의 기본 형식
SELECT [열 이름]
FROM [테이블 1]
<LEFT, RIGHT, FULL> OUTER JOIN [테이블 2] ON [테이블 1.열] = [테이블 2.열]
WHERE [검색 조건]
LEFT OUTER JOIN 문의 기본 개념 알아보기
OUTER JOIN은 LEFT, RIGHT, FULL 중 한 옶션을 지정해야 합니다. LEFT, RIGHT는 기준 테이블을 정하는 것을 의미합니다. 예를 들어 A, B테이블이 좌우에 있다고 생각했을 때 A 테이블을 기준으로 B 테이블을 조인하고 싶다면 LEFT를 사용하고, B 테이블을 기준으로 A 테이블을 조인하고 싶다면 RIGHT를 사용합니다.
LEFT OUTER JOIN 문 사용하기
-- 2개 테이블을 LEFT OUTER JOIN 쿼리 실행 : 기준 테이블(nasdaq_company)
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM nasdaq_company AS a
LEFT OUTER JOIN industry_group_symbol AS b ON a.symbol = b.symbol
LEFT OUTER JOIN 기준 테이블 데이터 추출
만약 기준 테이블에 있는 데이터만 추출하려면 LEFT OUTER JOIN 결과에서 NULL 데이터만 추출하면 됩니다.
-- 2개 테이블을 LEFT OUTER JOIN하고 기준 테이블 데이터만 추출 : NULL 필터링
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM nasdaq_company AS a
LEFT OUTER JOIN industry_group_symbol AS b ON a.symbol = b.symbol
WHERE b.symbol IS NULL
RIGHT OUTER JOIN 문의 기본 개념 알아보기
RIGHT OUTER JOIN 문 사용하기
-- 2개 테이블을 RIGHT OUTER JOIN 쿼리 실행 : 기준 테이블(nasdaq_company)
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM industry_group_symbol AS a
RIGHT OUTER JOIN nasdaq_company AS b ON a.symbol = b.symbol
RIGHT OUTER JOIN 기준 테이블 데이터 추출
만약 기준 테이블에 있는 데이터만 추출하려면 RIGHT OUTER JOIN 결과에서 NULL 데이터만 추출하면 됩니다.
-- 2개 테이블을 RIGHT OUTER JOIN하고 기준 테이블 데이터만 추출 : NULL 필터링
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM industry_group_symbol AS a
RIGHT OUTER JOIN nasdaq_company AS b ON a.symbol = b.symbol
WHERE a.symbol IS NULL
FULL OUTER JOIN 문의 기본 개념 알아보기
FULL OUTER JOIN은 LEFT OUTER JOIN과 RIGHT OUTER JOIN을 합친 것입니다. 양쪽 테이블의 일치하지 않는 행도 모두 검색합니다. 즉 조건 조건에 일치하지 않는 항목과 일치하는 항목 모두가 표시됩니다.
FULL OUTER JOIN 문 사용하기
-- 2개 테이블을 FULL OUTER JOIN 쿼리 실행 : 기준 테이블(nasdaq_company)
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM industry_group_symbol AS a
FULL OUTER JOIN nasdaq_company AS b ON a.symbol = b.symbol
FULL OUTER JOIN 양쪽 테이블 데이터 추출
FULL OUTER JOIN을 사용해 LEFT 테이블과 RIGHT 테이블에 있는 데이터만 추출하려면 NULL 데이터를 필터링해서 원하는 결과를 검색할 수 있습니다.
-- 2개 테이블을 FULL OUTER JOIN하고 각 테이블의 데이터만 추출 : NULL 필터링
SELECT
a.symbol AS a_symbol,
b.symbol AS b_symbol
FROM nasdaq_company AS a
FULL OUTER JOIN industry_group_symbol AS b ON a.symbol = b.symbol
WHERE a.symbol IS NULL
OR b.symbol IS NULL
교차 조인
자주 사용하지는 않지만, 각 테이블의 모든 경우의 수를 조합한 데이터가 필요할 경우 교차 조인(CROSS JOIN)을 사용할 수 있습니다. 교차 조인은 카르테시안 곱(cartesian product)이라고도 합니다.
-- 교차 조인의 기본 형식
SELECT [열 이름]
FROM [테이블 1]
CROSS JOIN [테이블 2]
WHERE [검색 조건]
기본 형식을 자세히 보면 FROM 문에는 조인 조건이 없습니다. 한 테이블에 저장된 모든 행이 다른 테이블의 행을 조인하므로 조인 조건이 필요 없습니다.
교차 조인의 기본 개념 알아보기
교차 조인 사용하기
-- CROSS JOIN 쿼리 실행
SELECT
a.num, b.name
FROM doit_cross1 AS a
CROSS JOIN doit_cross2 AS b
-- CROSS JOIN에 WHERE 문 사용
SELECT
a.num, b.name
FROM doit_cross AS a
CROSS JOIN doit_cross2 AS b
WHERE a.num = 1
셀프 조인
셀프 조인은(SELF JOIN)은 같은 테이블을 사용하는 특수한 조인이다. 셀프 조인을 사용하는 방법은 지금까지 배운 조인과 같습니다. 한 가지 주의 사항은 별칭을 사용해야 한다는 것입니다. 만약 별칭을 사용하지 않음면 오류가 발생합니다.
셀프 조인의 기본 개념 알아보기
셀프 조인 사용하기
-- SELF JOIN 쿼리 실행 예 1
SELECT
a.symbol, b.company_name
FROM nasdaq_company AS a
INNER JOIN nasdaq_company AS b
ON a.symbol = b.symbol
-- SEFT JOIN 쿼리 실행 예 2
SELECT
a.[date] AS a_date,
a.[close] AS a_close,
b.[date] AS b_date,
b.[close] AS b_close,
b.[close] - a.[close] AS diff_close
FROM stock AS a
LEFT OUTER JOIN stock AS b ON a.date = dateadd(day, -1, b.date)
WHERE a.symbol = 'MSFT' AND b.symbol = 'MSFT'
AND a.date >= '2021-10-01' AND a.date < '2021-11-01'
AND b.date >= '2021-10-01' AND b.date < '2021-11-01'
실전 SQL
퀴즈 1. industry_group 테이블에서 industry 열의 데이터가 Oil에 해당하는 symbol을 industry_group_symbol 테이블에서 검색한 다음, nasdaq_company 테이블에서 해당 symbol의 company_name을 검색하세요.
SELECT
a.industry, b.symbol, c.sector, c.company_name
FROM industry_group AS a
INNER JOIN industry_group_symbol AS b ON a.num = b.num
INNER JOIN nasdaq_company AS c ON b.symbol = c.symbol
WHERE a.industry = 'Oil'
퀴즈 2. nasdaq_company 테이블에서 industry_group_symbol 테이블에 포함되지 않는 symbol, industry, company_name 목록을 검색하세요.
SELECT
a.symbol, industry, a.company_name
FROM nasdaq_company AS a
LEFT OUTER JOIN industry_group_symbol AS b ON a.symbol = b.symbol
WHERE b.symbol IS NULL