Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] PostgreSQL - Join, Union, Except

Published
6 min readView as Markdown
[ 살펴보기 ] PostgreSQL - Join, Union, Except
C

A developer living in Busan, Korea

데이터 조회를 할 때 조회를 하는 대상이 되는 테이블이 2개 이상일 때 join statement를 통해 조회를 수행할 수 있다.

INNER JOIN

두 테이블을 기준으로 조회를 수행할 때 두 테이블에 공통적으로 존재하는 데이터만 조회한다. 예를 들어 다음 두 테이블이 존재한다고 가정해 보자.

TABLE library_a {
    id int, 
    name text,
    category text,
    available boolean
}

TABLE library_b {
    id int,
    name text,
    category text,
    available boolean
}

위의 두 테이블이 존재하는 상황에서 library_a에도 있고 library_b에도 있는 도서를 조회하고자 한다면 INNER JOIN을 통해 조회할 수 있다.

SELECT * FROM library_a INNER JOIN library_b 
ON library_a.name = library_b.name;

위의 예제에서 볼 수 있듯이 inner join을 통해 library_a와 library_b 두 개의 테이블이 조회 대상이 되고 name column이 서로 같은 데이터가 두 개의 테이블에서 모두 조회된다.

여기서 추가 서치 조건이 필요하면 다음과 같이 추가 서치 조건 역시 추가할 수 있다. 다음은 library_a, library_b 두 개의 테이블에서 이름이 같은 도서를 조회하되 library_a에서 현재 대여가 불가능한 상태인 도서만 조회한다.

SELECT * FROM library_a INNER JOIN library_b 
ON library_a.name = library_b.name;
WHERE library_a.available = FALSE;

LEFT JOIN

Inner join과는 다르게 left join은 왼쪽에 선언된 table을 데이터를 모두 조회하되 left join으로 연결된 table에선 조건에 부합하는 데이터만 조회 결과에 포함된다.

예를들어 각 테이블에 존재하는 데이터가 다음과 같다고 가정해보자.

/* 테이블 이름 */
library_a 

/* 테이블 데이터 */
id   name     category     available
1    'book1'  'category1'  t
2    'book2'  'category2'  t
3    'book3'  'category3'  f
4    'book4'  'category3'  f
/* 테이블 이름 */
library_b 

/* 테이블 데이터 */
id   name     category     available
1    'book1'  'category1'  f
2    'book2'  'category2'  t
3    'book3'  'category3'  t
5    'book5'  'category3'  t

위의 데이터를 기준으로 다음 SQL query를 실행하면 결과는 아래와 같다.

SELECT * FROM library_a LEFT JOIN library_b 
ON library_a.name = library_b.name;
 id | name  | category  | available | id | name  | category  | available
----+-------+-----------+-----------+----+-------+-----------+-----------
  1 | book1 | category1 | t         |  1 | book1 | category1 | f
  2 | book2 | category2 | t         |  2 | book2 | category2 | t
  3 | book3 | category3 | f         |  3 | book3 | category3 | t
  4 | book4 | category3 | f         |    |       |           |

결과에서 볼 수 있듯이 left join을 통해 조회을 수행하면 기준이 되는 table ( 위의 예제에서 library_a )의 data는 모두 조회하고 left join의 대상이 되는 table ( 위의 예제에서 library_b )에서 ON 구문에서 지정한 조건에 부합하는 데이터가 있으면 함께 조회가 되고 없으면 해당 record가 null로 처리된다. 위의 예제에서 library_b table에는 name이 book4인 데이터가 없으므로 해당 record는 null이 return되고 있다.

RIGHT JOIN

Left join은 left join 구문 왼쪽의 테이블을 기준으로 조회를 했다면 right join은 right join 구문 오른쪽의 테이블을 기준으로 조회를 수행한다.

SELECT * FROM library_a RIGHT JOIN library_b 
ON library_a.name = library_b.name;

위의 sql statement를 실행 했을 때 결과는 다음과 같다.

 id | name  | category  | available | id | name  | category  | available
----+-------+-----------+-----------+----+-------+-----------+-----------
  1 | book1 | category1 | t         |  1 | book1 | category1 | f
  2 | book2 | category2 | t         |  2 | book2 | category2 | t
  3 | book3 | category3 | f         |  3 | book3 | category3 | t
    |       |           |           |  5 | book5 | category3 | t

Right join은 right join 구문 오른쪽에 선언된 table을 기준으로 조회를 하기에 결과에서 볼 수 있듯이 library_a table에 name이 book5라는 데이터가 없으므로 해당 record에선 null이 return된다.

FULL OUTER JOIN

Full outer join을 통해 left join과 right join을 합친 결과를 조회할 수 있다. 다시 두 테이블의 데이터를 살펴보자.

/* 테이블 이름 */
library_a 

/* 테이블 데이터 */
id   name     category     available
1    'book1'  'category1'  t
2    'book2'  'category2'  t
3    'book3'  'category3'  f
4    'book4'  'category3'  f
/* 테이블 이름 */
library_b 

/* 테이블 데이터 */
id   name     category     available
1    'book1'  'category1'  f
2    'book2'  'category2'  t
3    'book3'  'category3'  t
5    'book5'  'category3'  t

두 테이블이 가진 data list과 위와 같을 때 full outer join을 실행하면 결과는 다음과 같다.

 id | name  | category  | available | id | name  | category  | available
----+-------+-----------+-----------+----+-------+-----------+-----------
  1 | book1 | category1 | t         |  1 | book1 | category1 | f
  2 | book2 | category2 | t         |  2 | book2 | category2 | t
  3 | book3 | category3 | f         |  3 | book3 | category3 | t
  4 | book4 | category3 | f         |    |       |           |
    |       |           |           |  5 | book5 | category3 | t

위의 결과에서 볼 수 있듯이 left join의 결과와 right join의 결과가 합쳐진 결과가 조회 되는 것을 볼 수 있다. 각각의 테이블의 데이터를 모두 조회하되 ON 구문에 지정된 조건에 부합하는 data가 없으면 해당 data row는 null을 반환한다.

추가 조건문 적용

Join을 사용해 조회한 결과 값에 추가 조건 filter를 적용해 원하는 데이터를 좀 더 세분화할 수도 있다.

SELECT * FROM library_a LEFT JOIN library_b 
ON library_a.name = library_b.name
WHERE library_a.available = 'f';

예를들어 위의 예제와 같이 library_a table을 기준으로 library_b를 left join한 결과를 기준으로 다시 library_a table의 데이터 중 available이 false인 데이터를 filter하고 있다.

 id | name  | category  | available | id | name  | category  | available
----+-------+-----------+-----------+----+-------+-----------+-----------
  3 | book3 | category3 | f         |  3 | book3 | category3 | t
  4 | book4 | category3 | f         |    |       |           |

다중 table join

Join을 통해 데이터를 조회할 때 2개 이상의 다중 table을 대상으로 join을 적용하여 조회할 수 있다. 테스트를 위해 library_c라는 데이터를 하나 더 생성하고 데이터는 다음과 같이 구성되어 있다.

/* 테이블 이름 */
library_c 

/* 테이블 데이터 */
id   name     category     available
1    'book1'  'category1'  t
2    'book2'  'category2'  f
3    'book3'  'category3'  t
6    'book6'  'category1'  t

그리고 이번에는 각 테이블에서 조회되는 column을 구분하기 위해 AS를 통해 조회되는 data column에 alias을 적용한다.

SELECT library_a.name AS Aname, 
library_b.name AS Bname,
library_c.name AS Cname, 
library_a.available AS Aavailable, 
library_b.available AS Bavailable, 
library_c.available AS Cavailable 
FROM library_a LEFT JOIN library_b
ON library_a.name = library_b.name LEFT JOIN library_c 
ON library_b.name = library_c.name;

위의 sql statement를 실행하면 결과는 다음과 같다.

 aname | bname | cname | aavailable | bavailable | cavailable
-------+-------+-------+------------+------------+------------
 book1 | book1 | book1 | t          | f          | t
 book2 | book2 | book2 | t          | t          | f
 book3 | book3 | book3 | f          | t          | t
 book4 |       |       | f          |            |

Join 뿐만 아니라 union, except, intersect를 통해서도 다중 테이블을 대상으로 조회를 수행할 수 있다.

UNION

다중 테이블에서 조회를 수행하며 default로 중복되는 데이터는 모두 결과에 포함 시키지 않고 하나만 포함한다. 다음 예제는 library_a, library_b에서 category column 데이터를 조회하는 예제다.

SELECT category FROM library_a UNION SELECT category FROM library_b;

위의 sql statement를 실행하면 조회 결과는 다음과 같다.

 category
-----------
 category3
 category1
 category2

만약 중복된 값일 지라면 결과에 모두 포함하고자 한다면 UNION ALL을 통해 조회를 수행한다.

SELECT category FROM library_a UNION ALL SELECT category FROM library_b;

위의 sql statement를 실행했을 때 조회 결과는 다음과 같다.

 category
-----------
 category1
 category2
 category3
 category3
 category2
 category3
 category3

Except

여러 table을 대상으로 조회를 할 때 첫 번째 테이블에만 존재하는 데이터를 조회하고 싶을 때 except를 통해 조회할 수 있다. 아래는 library_a table에만 존재하는 name을 조회하는 예제다.

SELECT name FROM library_a EXCEPT SELECT name FROM library_b;

위의 sql statement를 실행하면 결과는 다음과 같다. book4라는 이름의 데이터는 library_a table에만 존재하므로 조회 결과로 book4 데이터만 조회된 것을 확인할 수 있다.

 name
-------
 book4

Intersect

Except와 반대로 여러 table에 공통으로 존재하는 데이터만 조회하고자 할 때는 intersect를 사용한다.

SELECT name FROM library_a INTERSECT SELECT name FROM library_b;

library_a table과 library_b table에 공통으로 존재하는 데이터는 book1, book2, book3이므로 위의 sql statement를 실행했을 때 조회 과는 다음과 같다.

 name
-------
 book1
 book2
 book3

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts