자격증/SQLD

[SQL 첫걸음] 31강. 집합 연산

동호다찌 2022. 4. 4. 09:31

집합 연산

  • 관계형 모델을 채택한 데이터베이를 관계형 데이터베이스라 부릅니다.
  • 이때 관계형 모델에서의 관계형은 수학 집합론의 관계형 이론에서 유래했습니다.
  • 쉽게 설명하면 관계형 데이터베이스는 결국 데이터를 집합으로 간주해 다루기 쉽게 하는 목적에서 나온 개념입니다.

SQL과 집합

  • SQL에서 하나의 행을 하나의 요소, 그리고 복수의 행을 반환한 결과 전체를 하나의 집합이라 생각하면 됩니다.

UNION으로 합집합 구하기

합집합이란 집합 연산에서 집합끼리 서로 더한 것을 의미합니다.

UNION

SQL에서는 합집합을 계산하기 위해 UNION 키워드를 사용합니다.

  • 아래와 같이 테이블 sample71_a과 sample71_b가 존재한다고 가정해봅시다.
--sample71_a
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+

--sample71_b
+------+
| b    |
+------+
|    2 |
|   10 |
|   11 |
+------+
  • UNION 키워드를 사용하여 두 테이블 sample71_a와 sample71_b의 합집합을 구하는 방법은 아래와 같습니다.
mysql > SELECT * FROM sample71_a UNION SELECT * FROM sample71_b;

+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
|   10 |
|   11 |
+------+
5 rows in set (0.00 sec)

이때 유의할 점은 세미콜론(;)은 명령문의 종료를 의미하기 때문에 꼭 맨 마지막에 붙여야 한다는 것입니다.

  • 애스터리스크(*)를 사용하지 않고 아래와 같이 열을 따로 지정하여 집합의 요소가 될 데이터를 맞춰줄 수도 있습니다.
mysql > SELECT a FROM sample71_a UNION SELECT b FROM sample71_
  • UNION 명령을 사용할 때 나열 순서는 합집합의 결과에 영향을 주지 않습니다.
  • 따라서 결괏값 자체는 동일하지만 결괏값의 나열 순서는 달라질 수 있습니다.

UNION을 사용할 때의 ORDER BY

  • UNION 키워드를 사용하면 ORDER BY 명령을 가장 마지막 SELECT 명령에만 사용할 수 있습니다.
  • 또한 합집합의 결과를 정렬하는 것이기 때문에 지정한 열의 별명(AS)을 동일하게 지정하여 어떤 열을 정의해야 하는 지 정확하게 ORDER BY 명령으로 넘겨줘야 합니다.
mysql > SELECT a AS age FROM sample71_a 
         UNION 
        SELECT b AS age FROM sample71_b 
         ORDER BY age DESC;

+------+
| age  |
+------+
|   11 |
|   10 |
|    3 |
|    2 |
|    1 |
+------+
5 rows in set (0.00 sec)
  • 이는 결국 SELECT 구의 내부처리 순서가 ORDER BY 구보다 빠르기 때문입니다.

UNION ALL

  • UNION 키워드는 기본적으로 수학의 합집합과 동일하기 때문에 중복된 요소를 제거하여 결괏값을 반환합니다.
  • 그러나 만약 중복된 요소도 결괏값으로 얻고 싶다면 UNION ALL 키워드를 사용하면 됩니다.
  • 이는 중복을 제거하는 DISTINCT 키워드와 반대입니다.
  • 기본적으로 ALL 키워드가 생략되어 있기 때문에 SELECT 명령을 사용할 때 DISTINCT 키워드를 추가하지만 중복된 값을 반환합니다.
  • 반대로 UNION의 경우 DISTINCT 키워드가 기본이라고 생각하면 편합니다.
  • 그러나 실제로 DISTINCT 명령이 생략되어 있는 것은 아니기 때문에 UNION DISTINCT와 같은 명령은 오류를 반환하니 유의해야 합니다.
  • 테이블 sample71_a와 sample71_b의 중복된 요소를 포함한 합집합 결과를 확인하는 방법은 아래와 같습니다.
  • 그러면 앞서 중복되어 제거되었던 값 2가 이번에는 중복되어 결괏값으로 반횐된 것을 확인할 수 있습니다.
mysql > SELECT a AS age FROM sample71_a 
         UNION ALL 
        SELECT b AS age FROM sample71_b ORDER BY age DESC;

+------+
| age  |
+------+
|   11 |
|   10 |
|    3 |
|    2 |
|    2 |
|    1 |
+------+
6 rows in set (0.00 sec)
  • UNION 키워드는 내부적으로 결국 중복된 값이 있는 지 검사하는 처리가 필요하기 때문에 중복값이 없는 경우에는 UNION 키워드보다 UNION ALL 키워드를 사용하는 게 성능이 더 좋습니다.

교집합과 차집합

  • SQL에서 합집합과 마찬가지로 교집합과 차집합을 구할 수 있습니다.

교집합이란 두 개의 집합이 겹치는 부분, 다시 말해 공통 부분을 의미하며 차집합이란 하나의 집합을 기준으로 다른 쪽의 집합을 제거하고 남은 부분, 다시 말해 기준이 되는 집합에서 다른 집합과 공통된 부분을 제거하고 남은 순수 부분을 의미합니다.

  • 차집합의 결과가 아무런 요소도 존재하지 않는 공집합인 경우 이는 곧 두 개의 집합이 완전히 같다는 걸 의미합니다.
  • 교집합을 구하기 위해서는 INTERSECT 키워드를 사용하며 차집합을 구하기 위해서는 EXCEPT 키워드를, Oracle에서는 MINUS 키워드를 사용합니다.
  • MySQL에는 별도의 키워드가 존재하지 않기 때문에 직접 이를 계산하여 구해야 합니다.

MySQL에서 교집합과 차집합을 아래와 같이 EXISTS 및 NOT EXISTS 키워드를 통한 상관 서브쿼리를 활용하여 구현할 수 있습니다.

--sample71_a
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+

--sample71_b
+------+
| b    |
+------+
|    2 |
|   10 |
|   11 |
+------+
// a에서 b의 교집합
mysql> SELECT * FROM sample71_a 
        WHERE EXISTS ( SELECT * FROM sample71_b 
                        WHERE sample71_b.b = sample71_a.a );
+------+
| a    |
+------+
|    2 |
+------+
1 row in set (0.00 sec)

// a에서 b의 차집합
mysql> SELECT * FROM sample71_a 
        WHERE NOT EXISTS ( SELECT * 
                             FROM sample71_b WHERE sample71_a.a = sample71_b.b );
+------+
| a    |
+------+
|    1 |
|    3 |
+------+
2 rows in set (0.00 sec)