1. 집합 연산자의 종류
두 개 이상의 테이블에서 JOIN 을 사용하지 않고 연관된 데이터를 조회하는 방법 중 하나로 집합 연산자 (Set Operator) 를 사용하는 방법이다. 기존의 JOIN 에서는 FROM 절에 검색하고자 하는 테이블을 나열하고, WHERE 절에 JOIN 조건을 기술하여 원하는 데이터를 조회할 수 있었다. 하지만 집합 연산자는 여러 개의 질의의 결과를 연결하여 하나로 결합하는 방식을 사용한다. 즉, 집합 연산자는 2개 이상의 질의 결과를 하나의 결과로 만들어 준다.
일반적으로 집합 연산자를 사용하는 상황은 1)서로 다른 테이블에서 유사한 형태의 결과를 반환하는 것을 하나의 결과로 합치고자 할 때와 2)동일 테이블에서 서로 다른 질의를 수행하여 결과를 합치고자 할 때 사용할 수 있다. 이외에도 튜닝관점에서 실행계획을 분리하고자 하는 목적으로도 사용할 수 있다.
집합 연산자를 사용하기 위해서는 다음 제약조건을 만족해야 한다.
- SELECT 절의 컬럼수가 동일해야 한다.
- SELECT 절의 동일 위치에 있는 컬럼의 데이터 타입이 상호 호환 가능해야 한다.
2. 집합 연산자의 연산
집합 연산자는 개별 sql 문의 결과 집합에 대해 합집합 (UNION/UNION ALL), 교집합 (INTERSECT), 차집합 (EXCEPT) 으로 집합간의 관계를 가지고 작업을 한다.
집합 연산자를 가지고 연산한 결과는 [그림 II-2-5] 와 같다. 그림의 왼쪽에 존재하는 R1, R2 는 각각의 SQL 문을 실행해서 생성된 개별 결과 집합을 의미한다.
그림에서 보면 UNION ALL 을 제외한 다른 집합 연산자에서는 SQL 문의 결과 집합에서 먼저 중복된 건을 배제하는 작업을 수행한 후 집합 연산을 적용한다. UNION 연산에서 R1 = {1,2,3,5}, R2 = {1,2,3,4} 가 되고, 이것의 합집합 (R1 ∪ R2) 의 결과는 {1,2,3,4,5} 이다. UNION ALL 연산은 중복에 대한 배제 없이 2 개의 결과 집합을 단순히 합친 것과 동일한 결과이다. UNION ALL 의 결과는 {1,1,1,2,2,3,3,5,1,1,2,2,2,3,4} 이다.
INTERSECT 연산에서 R1 = {1,2,3,5}, R2 = {1,2,3,4} 의 교집합 (R1 ∩ R2) 의 결과는 {1,2,3} 이다.
EXCEPT 연산에서는 R1 = {1,2,3,5}, R2 = {1,2,3,4} 의 차집합 (R1 - R2) 의 결과는 {5} 이다. EXCEPT 연산에서는 순서가 중요하다. 만약 순서가 바뀌어서 R2 - R1 의 차집합이었다면 결과는 {4} 가 된다.
집합 연산자를 사용하여 만들어지는 SQL문의 형태는 다음과 같다.
SELECT 칼럼명1, 칼럼명2, ...
FROM 테이블명1
[WHERE 조건식]
[GROUP BY 칼럼(Column)이나 표현식
HAVING 그룹조건식]
집합 연산자
SELECT 칼럼명1, 칼럼명2, ...
FROM 테이블명2
[WHERE 조건식]
[GROUP BY 칼럼(Column)이나 표현식
HAVING 그룹조건식]
[ORDER BY 1, 2 [ASC 또는 DESC]] ;
집합 연산자는 사용상의 제약조건을 만족한다면 어떤 형태의 SELECT 문이라도 이용할 수 있다. ORDER BY 는 집합 연산을 적용한 최종 결과에 대한 정렬 처리이므로 가장 마지막 줄에 한번만 기술한다.
3. 집합 연산 예제
1) UNION (합집합)
SELECT X, Y, Z FROM A WHERE X = '001'
UNION
SELECT X, Y, Z FROM A WHERE X = '002';
- 합집합을 WHERE 절에 IN 또는 OR 연산자로도 변환이 가능하다. 다만 IN 또는 OR 연산자를 사용할 경우에는 결과의 표시 순서가 달라질 수 있다. 동일한 표시 순서를 원한다면 ORDER BY 절을 사용해서 명시적으로 정렬 순서를 정의할 수 있다.
SELECT X, Y, Z FROM A WHERE X = '001' OR X = '002';
SELECT X, Y, Z FROM A WHERE X IN ('001', '002');
2) UNION ALL (합집합)
SELECT X, Y, Z FROM A WHERE X = '001'
UNION
SELECT X, Y, Z FROM A WHERE Y = '999';
- 예제2 또한 OR 연산자를 사용하여 SQL문을 변경할 수 있다. 다만 조건절에서 서로 다른 컬럼을 사용했기 때문에 IN 연산자는 사용할 수 없다.
SELECT X, Y, Z FROM A WHERE X = '001' OR Y = '999';
- UNION 연산자 대신 UNION ALL 연산자를 사용하면 결과 집합에서 중복을 제외시키지 않기 때문에 더 많은 행이 조회된다.
SELECT X, Y, Z FROM A WHERE X = '001'
UNION ALL
SELECT X, Y, Z FROM A WHERE Y = '999';
3) 그룹함수
SELECT X, AVG(Y) FROM A GROUP BY X
UNION
SELECT Z, AVG(Y) FROM A GROUP BY Z
ORDER BY 1;
- 그룹함수 또한 집합 연산자에서 사용이 가능하다. 집합 연산자의 결과를 표시할 때 HEADING 부분은 첫번째 SQL 문에서 사용된 HEADING 이 적용된다.
4) MINUS/EXCEPT (차집합)
- Oracle
SELECT X, Y, Z FROM A WHERE X = '001'
MINUS
SELECT X, Y, Z FROM A WHERE Y = '999'
ORDER BY 1, 2, 3;
- SQL Server 에서는 MINUS 대신에 EXCEPT 를 사용할 수 있다.
- 차집합은 앞의 집합의 결과에서 뒤의 집합의 결과를 빼는 것이다. 연산자의 앞에 오는 SQL 문의 조건은 만족하고, 뒤에 오는 SQL 문의 조건은 만족하지 않는 SQL 문과 동일한 결과를 얻을 수 있다.
SELECT X, Y, Z FROM A WHERE X = '001' AND Y <> '999';
- MINUS 연산자는 NOT EXISTS 또는 NOT IN 서브쿼리를 이용한 SQL 문으로 변경이 가능하다.
5) INTERSECT (교집합)
SELECT X, Y, Z FROM A WHERE X = '001'
INTERSECT
SELECT X, Y, Z FROM A WHERE Y = '999';
- 교집합의 결과는 두 조건을 모두 만족하는 데이터의 집합이다. 연산자의 앞에 오는 SQL 문의 조건과 뒤의 SQL 문의 조건을 만족하는 SQL 문과 동일한 결과를 얻을 수 있다.
SELECT X, Y, Z FROM A WHERE X = '001' AND Y = '999';
- INTERSECT 연산자는 EXISTS 또는 IN 서브쿼리를 이용한 SQL 문으로 변경할 수 있다.
SELECT X, Y, Z FROM A T1
WHERE T1.X = '001'
AND EXISTS (SELECT 1 FROM A T2
WHERE T2.X = T1.X
AND T2.Y = '999');
SELECT X, Y, Z FROM A
WHERE X = '001'
AND Y IN (SELECT Y FROM A WHERE Y = '999');