집합 연산

banjjoknim·2021년 2월 27일
0

31강. 집합 연산

지금부터는 복수의 테이블을 사용해 데이터를 검색하는 방법에 관해 알아본다.

  • RDBMS의 창시자인 에드거 커드는 관계형 모델을 고안한 인물이기도 하다.
  • 관계형 모델을 채택한 데이터베이스를 관계형 데이터베이스라 부른다.
  • 관계형 모델에서의 관계형은 수학 집합론의 관계형 이론에서 유래했다.
  • 집합론이라고 거창하게 말하지만 실질적으로는 데이터베이스의 데이터를 집합으로 간주해 다루기 쉽게 하자는 것에 지나지 않는다.

1. SQL과 집합

집합이라 하면 ANDOR를 설명할 때 등장했던 벤 다이어그램을 떠올리면 쉽다.

  • 벤 다이어그램에서는 하나의 원이 곧 하나의 집합이다.
  • 원 안에는 몇 가지 요소가 포함되는데, 원 안에 적혀있는 숫자가 요소에 해당한다.
  • 한편 데이터베이스에서는 테이블의 행이 요소에 해당한다.
  • 행은 여러 개의 열로 구성되는 경우도 있으므로, 수치 상으로는 복수의 값이 존재한다.
  • 하지만 집합의 요소라는 측면에서 보면 하나의 행이 곧 하나의 요소가 된다.
  • SELECT 명령을 실행하면 데이터베이스에 질의하며 그 결과 몇 개의 행이 반환된다.
  • 이때 반환된 결과 전체를 하나의 집합이라고 생각하면 된다.

2. UNION으로 합집합 구하기

  • 집합의 연산에는 합집합이라는 것이 있다. 이는 집합을 서로 더한 것을 말한다.
  • 우선 간단한 숫자를 모아놓은 집합을 이용해 설명한다.
  • A와 B라는 두 개의 집합이 존재한다고 했을 때, A 집합에는 [1, 2, 3]이라는 세 개의 요소가, B 집합에는 [2, 10, 11]이라는 세 개의 요소가 있다.
  • 그중 2라는 요소는 A에도 B에도 모두 존재한다.
  • 집합 A와 B의 합집합을 구하면 그 결과는 [1, 2, 3, 10, 11]이 된다.
  • 벤 다이어 그램으로 설명하면 두 개의 집합을 모두 합한 부분이 합집합의 결과이다.
  • 이때 두 개 집합에서 겹쳐지는 부분, 즉 '요소 2'가 계산 결과로는 한 개밖에 나타나지 않는다는 점이 핵심이다.
  • 즉, 단순하게 서로 더하면 [1, 2, 3, 2, 10, 11]과 같이 6개의 요소가 되지만 합집합에서는 그렇지 않다.

UNION

  • SQL에서는 SELECT 명령의 실행 결과를 하나의 집합으로 다룰 수 있다.
  • 합집합을 계산할 경우에는 수학에서 사용하던 U기호 대신 UNION 키워드를 사용한다.
  • U는 U와 모양이 비슷해 기억하기 쉽다. 즉, 수학에서의 A U B는 SQL에서는 A UNION B라고 표현한다.
  • A나 B로 표현했지만 실제로는 SELECT 명령이다.
  • SELECT 명령의 결과를 집합으로 간주하고, UNION으로 합집합을 구할 수 있다.

두 개의 SELECT 명령을 UNION해서 합집합 구하기

SELECT * FROM smaple71_a
UNION
SELECT * FROM sample71_b;
  • 이때 두 개의 SELECT 명령을 하나의 명령으로 합치는 만큼, 세미콜론(;)은 맨 나중에 붙인다는 점에 주의해야 한다.
  • 만약 sample71_a 뒤에 세미콜론을 붙이면 도중에 명령이 끝나버리므로 제대로 실행되지 않는다.
  • 정리하면, 한 번의 쿼리 실행으로 두 개의 SELECT 명령이 내부적으로 실행되는 형식이다.
  • 이때 각 SELECT 명령의 실행결과(집합)를 합집합(UNION)으로 계산하여 최종적으로 결과를 반환한다.
UNION으로 두 개의 SELECT 명령을 하나로 연계해 질의 결과를 얻을 수 있다!
  • UNION을 이용하면 여러 개의 SELECT 명령을 하나로 묶을 수 있다.
  • 1+2+3...처럼 연속해서 더하는 것과 같은 형식이다.
  • 이때 각각의 SELECT 명령의 열의 내용은 서로 일치해야 한다.
  • 예를 들어 sample71_a와 sample71_b의 경우, 열 이름은 서로 다르지만 열 개수와 자료형이 서로 같기 때문에 일치한다고 말할 수 있다.
  • 반면 다음과 같이 완전히 열 구성이 다른 테이블을 UNION으로 묶을 수는 없다.
SELECT * FROM sample71_a
UNION
SELECT * FROM sample71_b
UNION
SELECT * FROM sample31;
  • 다만 전체 데이터를 반환하는 애스터리스크(*)를 쓰지 않고, 열을 따로 지정하여 각 SELECT 명령에서 집합의 요소가 될 데이터를 서로 맞춰주면 UNION으로 실행할 수 있는 쿼리가 된다. 예를 들면 다음과 같다.
SELECT a FROM sample71_a
UNION
SELECT b FROM sample71_b
UNION
SELECT age FROM sample31;
  • SELECT 명령들을 UNION으로 묶을 때 나열 순서는 합집합의 결과에 영향을 주지 않는다.
  • 따라서 아래의 명령들은 결과가 모두 같다.
  • 단, 결괏값의 나열 순서는 달라질 수도 있다.
  • ORDER BY를 지정하지 않은 SELECT 명령은 결과가 내부처리의 상황에 따라 바뀌기 때문이다.
SELECT * FROM sample71_a UNION SELECT * FROM sample71_b;
SELECT * FROM sample71_b UNION SELECT * FROM sample71_a;
  • UNION을 사용할 때에는 ORDER BY를 지정하는 방법에 주의해야 한다.

UNION을 사용할 때의 ORDER BY

  • UNION으로 SELECT 명령을 결합해 합집합을 구하는 경우, 각 SELECT 명령에 ORDER BY를 지정해 정렬할 수는 없다.
  • ORDER BY를 지정할 때는 마지막 SELECT 명령에만 지정하도록 한다.
  • 예를 들어 다음과 같은 쿼리를 실행하면 에러가 발생한다.

첫 번째 SELECT 명령에 ORDER BY를 지정할 수 없다

SELECT a FROM sample71_a ORDER BY a
UNION
SELECT b FROM sample71_b;
  • ORDER BY로 정렬할 수 없다는 뜻이 아니다. 합집합의 결과를 정렬하므로, 가장 마지막의 SELECT 명령에 ORDER BY를 지정해야 한다는 의미이다.

마지막의 SELECT 명령에 ORDER BY를 지정한다

SELECT a FROM sample71_a
UNION
SELECT b FROM sample71_b ORDER BY b;
  • 하지만 이 쿼리에서도 에러가 발생한다. ORDER BY를 지정할 수 있다고 해도 마지막의 SELECT 명령의 결과만 정렬하는 것이 아니고 합집합의 결과를 정렬하는 것이기 때문이다.
  • 이때 두 개의 SELECT 명령에서 열 이름이 서로 일치한다면 문제가 없겠지만 앞의 예제에서처럼 반드시 그렇다는 보장이 없다.
  • 이런 경우 서로 동일하게 별명을 붙여 정렬할 수 있다.
SELECT a AS c FROM sample71_a
UNION
SELECT b AS c FROM sample71_b ORDER BY c;
UNION으로 SELECT 명령을 연결하는 경우, 가장 마지막 SELECT 명령에 대해서만 ORDER BY 구를 지정할 수 있다!
ORDER BY 구에 지정하는 열은 별명을 붙여 이름을 일치시킨다!

UNION ALL

  • UNION은 합집합을 구하는 것이므로 두 개의 집합에서 겹치는 부분은 공통 요소가 된다.
  • 예를 들어 앞에서 살펴본 sample71_a와 sample71_b 예제에서는 양쪽 모두 2가 포함되어 있었다. 그리고 이들을 합집합 하면 2는 하나만 존재한다.
  • 두 명령의 실행 결과에 DISTINCT를 걸어 중복을 제거한 것과 같다고 생각하면 이해하기 쉬울 것이다.
  • 수학에서 말하는 집합은 중복값이 존재하지 않는 것을 전제로 한다.
  • 그래서 UNION을 한 결과에도 중복값이 제거되어 있다.
  • 하지만 경우에 따라서는 중복을 제거하지 않고 2개의 SELECT 명령의 결과를 그냥 합치고 싶은 때도 있을 것이다. 이러한 경우에는 UNION ALL을 사용한다.
  • SELECT 명령에서 중복을 제거할 때는 SELECT 구에 DISTINCT를 지정한다.
  • 이때 기본값은 ALL로, 명시적으로 지정하거나 생략할 수도 있다.
  • 즉, 중복을 제거하는 경우에는 DISTINCT, 중복을 제거하지 않고 모두를 반환하는 경우에는 ALL을 추가적으로 지정한다.
  • 즉, DISTINCTALL로 중복제거 여부를 지정할 수 있다는 점은 똑같지만, UNION의 기본동작은 ALL이 아닌 DISTINCT라는 점이 다르다.
  • 또한 UNION DISTINCT라는 문법은 허용되지 않으므로 주의해야 한다.

두 개의 SELECT 명령에 UNION ALL을 적용해 합집합 구하기

SELECT * FROM sample71_a
UNION ALL
SELECT * FROM sample71_b;
  • 위의 명령을 실행하면 2라는 값을 가진 행이 중복되어 표시된다.
  • UNION ALL은 두 개의 집합을 단순하게 합치는 것이다.
  • UNION에서는 이미 존재하는 값인지를 검사하는 처리가 필요한 만큼, UNION ALL 쪽이 성능적으로는 유리할 경우가 있다.
  • 즉, 중복값이 없는 경우에는 UNION ALL을 사용하는 편이 좋은 성능을 보여준다.

3. 교집합과 차집합

  • MySQL에서는 지원되지 않는다.
  • SQL을 이용해 교집합, 차집합도 구할 수 있다. 교집합은 INTERSECT를, 차집합은 EXCEPT를(Oracle의 경우는 MINUS) 사용한다.
  • 교집합이란 두 개의 집합이 겹치는 부분을 말하며, 공통 부분이라 불리기도 한다.
  • 차집합은 집합에서 다른 쪽의 집합을 제거하고 남은 부분이다.
  • 계산 대상이 되는 두 개의 집합에 공통부분이 존재하지 않으면 차집합을 구해도 결과는 바뀌지 않는다.
  • 또한 완전히 같은 집합끼리 차집합을 계산하면 아무런 요소도 존재하지 않는 공집합이 된다.
  • 차집합의 결과가 공집합인지 아닌지에 따라 두 개의 집합이 동일한지 아닌지를 알 수 있다.

profile
꿈꾸는 개발자

0개의 댓글