포스트

[SQL] DML: JOIN (feat. INNER, OUTER)

1. 조인

두 개 이상의 테이블동시에 질의를 할 수 있다. 이것은 조인(Join) 연산을 이용해 할 수 있는데, 이것은 한 테이블의 행에 다른 테이블의 행을 연결해 결합하는 연산이다.

가장 기본적으로 조인을 아래처럼 할 수 있다.

1
2
3
-- 카테시안 곱과 동일하다.
SELECT  *
FROM    Customer, Orders;
  • 질의 결과

1. 조인 Customer와 Orders 테이블의 합체

  • Customer와 Orders 테이블의 튜플 개수는 각각 5, 10이고 이들을 합체한 결과 테이블의 튜플 개수는 $5 \times 10 = 50$이다.
  • 두 테이블을 이처럼 아무 조건 없이 SELECT 시키면 관계대수의 카티전 프로덕트 연산이 된다.
  • 이렇게 얻은 결과는 논리에 맞지 않으므로, 적절한 조건을 걸어주어야 의미가 있다.

1.1. 조인 연산의 예시

질의 3-21 고객과 고객의 주문에 관한 데이터를 모두 보이시오.


정답

1
2
3
SELECT  *
FROM    Customer, Orders
WHERE   Customer.custid = Orders.custid; -- 사실 요즘 DBMS는 조건에 사용된 속성을 기준으로 결과가 자동으로 정렬이 된다.


질의 결과

  • orderid 오름차순으로 되어있음을 확인할 수 있다.
custidnameaddressphoneorderidcustidbookidsalepriceorderdate
1박지성영국 맨체스타000-5000-000111160002024-07-01
1박지성영국 맨체스타000-5000-0001213210002024-07-03
2김연아대한민국 서울000-6000-000132580002024-07-03
3김연경대한민국 경기도000-7000-000143660002024-07-04
4추신수미국 클리블랜드000-8000-0001547200002024-07-05
1박지성영국 맨체스타000-5000-0001612120002024-07-07
4추신수미국 클리블랜드000-8000-0001748130002024-07-07
3김연경대한민국 경기도000-7000-00018310120002024-07-08
2김연아대한민국 서울000-6000-0001921070002024-07-09
3김연경대한민국 경기도000-7000-00011038130002024-07-10


질의 3-22 고객과 고객의 주문에 관한 데이터를 고객번호 순으로 정렬하여 보이시오.


정답

1
2
3
4
SELECT    *
FROM      Customer, Orders
WHERE     Customer.custid = Orders.custid;
ORDER BY  Customer.custid;


질의 결과

custidnameaddressphoneorderidcustidbookidsalepriceorderdate
1박지성영국 맨체스타000-5000-000111160002024-07-01
1박지성영국 맨체스타000-5000-0001213210002024-07-03
1박지성영국 맨체스타000-5000-0001612120002024-07-07
2김연아대한민국 서울000-6000-000132580002024-07-03
2김연아대한민국 서울000-6000-0001921070002024-07-09
3김연경대한민국 경기도000-7000-000143660002024-07-04
3김연경대한민국 경기도000-7000-00018310120002024-07-08
3김연경대한민국 경기도000-7000-00011038130002024-07-10
4추신수미국 클리블랜드000-8000-0001547200002024-07-05
4추신수미국 클리블랜드000-8000-0001748130002024-07-07

열 이름을 표기하는 방법

Customer.custid, Orders.custid와 같이 테이블이름.열이름 형식으로 표현함으로써 해당 속성명이 어느 테이블의 속성인지 명시할 수 있다.



질의 3-23 고객의 이름과 고객이 주문한 도서의 판매가격을 검색하시오.


정답

1
2
3
SELECT  name, saleprice
FROM    Customer, Orders
WHERE   Customer.custid = Orders.custid;


질의 결과

namesaleprice
박지성6000
박지성21000
박지성12000
김연아8000
김연아7000
김연경6000
김연경12000
김연경13000
추신수20000
추신수13000

모든 SQL 질의의 결과는 단일 테이블이므로, 질의 3-23 결과 테이블에 SQL 문법을 적용할 수 있다.


질의 3-24 고객별로 주문한 모든 도서의 총 판매액을 구하고, 고객별로 정렬하시오.


정답

1
2
3
4
5
SELECT    name, SUM(saleprice)
FROM      Customer, Orders
WHERE     Customer.custid = Orders.custid
GROUP BY  name  -- Customer.name 으로 명시적으로 표현 가능하다.
ORDER BY  name;


질의 결과

nameSUM(saleprice)
김연경31000
김연아15000
박지성39000
추신수33000

2. 3개 이상의 테이블에 대한 조인

3개 이상의 테이블을 조인할 수 있다.



질의 3-25 고객의 이름과 고객이 주문한 도서의 이름을 구하시오.


정답

1
2
3
SELECT  Customer.name, Book.bookname
FROM    Customer, Orders, Book
WHERE   Orders.custid = Customer.custid AND Orders.bookid = Book.bookid;


질의 결과

namebookname
박지성축구의 역사
박지성축구의 이해
김연아피겨 교본
김연경배구 단계별기술
추신수야구의 추억
박지성축구 아는 여자
추신수야구를 부탁해
김연경Olympic Champions
김연아Olympic Champions
김연경야구를 부탁해

다음은 조인 과정 중 데이터들이 연결되는 모습을 나타낸 것이다.

2. 3개 이상의 테이블에 대한 조인 테이블 데이터들이 연결되는 모습



질의 3-26 가격이 20,000원인 도서를 주문한 고객의 이름과 도서의 이름을 구하시오.


정답

1
2
3
4
SELECT  Customer.name, Book.bookname
FROM    Customer, Orders, Book
WHERE   Orders.custid = Customer.custid AND Orders.bookid = Book.bookid 
        AND Book.price = 20000;


질의 결과

namebookname
추신수야구의 추억

3. INNER JOIN

일반적으로 조인이라고 하면 이 INNER JOIN을 말하는데, 지금까지는 조인을 하기 위해 FROM 절에서 쉼표로 테이블을 열거하였다.

이 방법 대신 INNER JOIN 문을 사용할 수 있다. 쉼표로 열거하는 기존의 방법도 INNER JOIN이라고 할 수 있지만, WHERE 조건을 생략하면 카티전 곱이 되기 쉽다는 단점이 있다.

참고

SQL에서는 주로 동등 조인을 사용한다.

3.1. 방법 1

  • 조인조건WHERE 절으로 들어가는 방법이다.
  • 지금까지 해왔던 조인 방식이다.
1
2
3
SELECT  '속성1', '속성2', etc
FROM    TABLE1, TABLE2
WHERE   '조인조건' AND '검색조건'

3.2. 방법 2

  • INNER JOIN 문을 사용하면 ON 문이 뒤따라와야 하며, 여기에는 조인조건을 명시한다.
  • 조인조건ON 구문을 이용해 따로 명시해야 하기 때문에, 가독성이 조금 더 좋아진다.
  • []: 생략 가능하다. 즉 INNER 키워드는 생략 가능하며, JOIN문은 기본적으로 INNER JOIN을 수행한다.
1
2
3
4
SELECT  '속성1', '속성2', etc
FROM    TABLE1 [INNER] JOIN TABLE2 
            ON '조인조건'
WHERE   '검색조건'



질의 3-26 (feat. INNER JOIN) 가격이 20,000원인 도서를 주문한 고객의 이름과 도서의 이름을 구하시오.

  • 기존 답
1
2
3
4
SELECT  Customer.name, Book.bookname
FROM    Customer, Orders, Book
WHERE   Orders.custid = Customer.custid AND Orders.bookid = Book.bookid 
        AND Book.price = 20000;


정답

1
2
3
4
5
6
7
SELECT  Customer.name, Book.bookname
FROM    Customer
    INNER JOIN  Orders
        ON Orders.custid = Customer.custid
    INNER JOIN  Book
        ON Orders.bookid = Book.bookid 
WHERE   Book.price = 20000;
  • INNER를 생략해 더 간단히 표기가 가능하다.
1
2
3
4
5
SELECT  Customer.name, Book.bookname
FROM    Customer 
          JOIN Orders ON Orders.custid = Customer.custid
          JOIN Book   ON Orders.bookid = Book.bookid 
WHERE   Book.price = 20000;
  • JOINON을 묶어서 더욱 간단히 표기할 수 있다.
1
2
3
4
SELECT  Customer.name, Book.bookname
FROM    Customer JOIN Orders JOIN Book
            ON Orders.custid = Customer.custid AND Orders.bookid = Book.bookid 
WHERE   Book.price = 20000;

3.3. 방법 1과 방법 2 비교

  • 두 방법 모두 사용할 수 있지만, 방법 2를 사용하는 것이 가독성에서 조금 더 뛰어다고 할 수 있다.
  • 현대 DBMS에서 INNER JOIN에 한해서는 두 방법은 성능 상에 차이가 거의 없다고 알려져 있다.

4. OUTER JOIN(외부조인)

4.1. 문법

외부 조인은 다음 문법으로 가능하다.

  • []: 생략 가능
    • 따라서 OUTER를 생략할 수 있다.
  • {}: |으로 구분된 조건들 중 하나를 선택해야 함
    • 이것을 아예 생략하면 INNER JOIN이 된다.
1
2
3
4
SELECT  '속성1', '속성2', etc
FROM    TABLE1 { LEFT | RIGHT | FULL } [OUTER] JOIN TABLE2 
            ON '조인조건'
WHERE   '검색조건'
  • LEFT RIGHT FULL은 관계대수에서 배웠던 그것으로, LEFT 외부조인을 수행하면 왼쪽 테이블의 튜플은 값으로 가득 채워지지만 오른쪽 테이블에는 NULL 값이 포함될 수 있다.

4.2. 예시

질의 3-23를 보면 도서를 주문하지 않은 고객 ‘박세리’는 결과에 포함되지 않는다. 이 고객까지 결과에 포함하려면 어떻게 해야 할까? 바로 외부 조인을 사용하면 된다.



질의 3-27 (3-23 변형) 도서를 구매하지 않은 고객을 포함하여, 고객의 이름과 고객이 주문한 도서의 판매 가격을 구하시오.


틀린 답(3-23의 정답)

1
2
3
4
5
SELECT  Customer.name, Orders.saleprice
FROM    Customer, Orders
WHERE   Customer.custid = Orders.custid;
-- Orders.custid에 주문하지 않은 고객의 custid가 없으므로,
-- 따라서 주문하지 않은 고객은 결과에 포함되지 않는다.


정답

1
2
3
4
SELECT  Customer.name, Orders.saleprice
FROM    Customer 
            LEFT OUTER JOIN 
        Orders ON Customer.custid = Orders.custid; -- WHERE과 같은 역할을 수행한다. JOIN문 사용 시 이처럼 ON을 사용한다.


질의 결과

namesaleprice
박지성6000
박지성21000
박지성12000
김연아8000
김연아7000
김연경6000
김연경12000
김연경13000
추신수20000
추신수13000
박세리NULL

  • ONWHERE 절과 같은 역할을 수행하고 있다.
  • 이처럼 JOIN 절과 함께라면 WHERE 대신 ON을 사용해야 한다.
이 포스팅은 작성자의 CC BY-NC 4.0 라이선스를 준수합니다.