제 2절 집합 연산자(SET OPERATOR)
두 개 이상의 테이블에서 조인을 사용하지 않고 연관된 데이터를 조회하는 방법 중에 또 다른 방법이 있는데, 바로 집합 연산자(Set Operator)를 사용하는 방법이다.
기존 조인에서는 FROM 절에 검색하고자 하는 테이블을 나열하고, WHERE 절에 조인 조건을 기술하여 원하는 데이터를 조회하였으나, 집합연산자를 사용하게 되면, 여러 개의 질의의 결과를 연결하여 하나로 결합하는 방식을 사용한다. 즉, 집합 연산자는 2개 이상의 질의 결과를 하나의 결과로 만들어주는 역할을 하게 된다.
집합 연산자는 SELECT 절의 칼럼 수가 동일하고 SELECT 절의 동일 위치에 존재하는 칼럼의 데이터 타입이 상호 교환 가능해야한다.
UNION, INTERSECT, EXCEPT 은 중복된 행은 하나의 행으로 만들지만, UNION ALL은 중복된 행을 하나의 행으로 만들어주지 않고, 중복된 결과가 항상 존재한다. 이는 중복에 대한 정렬 작업이 일어나지 않는다는 특징을 가진다. 글보다는 그림으로 이해하는 편이 훨씬 낫다.
집합연산자를 사용하여 쿼리문을 작성하는 형태의 예시는 다음과 같다.
SELECT PLAYER_NAME 선수명, BACK_NO 백넘버
FROM PLAYER
WHERE TEAM_ID = 'K02'
UNION
SELECT PLAYER_NAME 선수명, BACK_NO 백넘버
FROM PLAYER
WHERE TEAM_ID = 'K07'
ORDER BY 1;
집합연산자를 사용하여 쿼리문을 작성할 경우, 정렬 작업은 마지막 문장에 1번만 써준다.
합집합
합집합을 나타내는 쿼리문은 UNION, UNION ALL을 사용해도 되지만, WHERE 절에 IN 또는 OR 연산자로도 변환이 가능하다. 그러나 이를 사용할 경우에는 결과의 표시 순서가 달라질 수 있다. UNION 연산자로 쿼리를 작성했을 경우 K02인 결과가 나오고 K07인 결과가 뒤에 나오지만, IN 또는 OR 연산자를 사용할 경우에는 제일 마지막에 쓰인 조건 순으로 나온다. 예를 들어 WHERE TEAM_ID IN('K02', 'K07); 이거나 WHERE TEAM_ID = 'K02' OR TEAM_ID = 'K07'; 로 작성했을 경우, 07 결과가 먼저 나오고 그 뒤에 02 결과가 나오게 된다.
그런데 K02와 GK 라는 조건을 검색할 경우, 이는 서로 다른 칼럼이 사용되었으므로 UNION을 썼을때의 결과는 GK의 결과가 먼저 나오고, 그 다음 K02가 나오게 된다. 이는 서로 다른 칼럼에 조건을 사용했기 때문에 IN 연산자를 쓸 수 없고, OR 연산자를 사용할 경우, K02 의 결과가 먼저 나타나게 된다.
또한 UNION ALL의 집합연산자를 사용한 경우, 중복이 제거되지 않아 더 많은 행이 결과에 나타 날 수 있으며, 이 때 ORDER BY 절을 이용하여 정렬을 해보면, 중복된 결과를 확인해볼 수 있다. 웬만하면 집합연산자를 쓸 때, 정렬을 하는 것이 좋다.
또한 추가적으로 실제로 테이블에는 존재하지 않지만, 결과 행을 구분하기 위해 SELECT 절에 칼럼('구분코드')을 추가할 수 있다는 것을 아래의 쿼리에서 알 수 있다. 이와 같이 목적을 위해 SELECT 절에 임의의 칼럼을 추가하는 것은 다른 모든 SQL 문에서 적용이 가능하다.
SELECT 'P' 구분코드, POSITION 포지션, AVG(HEIGHT) 평균키
FROM PLAYER
GROUP BY POSITION
UNION
SELECT 'T' 구분코드, TEAM_ID 팀명, AVG(HEIGHT) 평균키
FROM PLAYER
GROUP BY TEAM_ID
ORDER BY 1;
또한 결과는 구분코드, 포지션, 평균키 라는 칼럼을 가진 결과가 생성되고, 팀명 대신 포지션이라는 말이 먼저 나왔기 때문에 결과에 첫번쨰 SQL문에서 사용된 HEADING이 적용되었다.
차집합
합집합을 살펴 보았고, 차집합을 살펴보자면 Oracle 에서는 MINUS가 사용되고, SQL Server에서는 EXCEPT가 사용된다. 논리연산자로는 <> 가 사용될 수 있다.
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE TEAM_ID = 'K02'
AND POSITION <> 'MF'
ORDER BY 1, 2, 3, 4, 5;
또한, NOT EXISTS 또는 NOT IN 서브쿼리를 이용한 SQL 문으로도 변경 가능하다.
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER X
WHERE X.TEAM_ID = 'K02'
AND NOT EXISTS (SELECT 1 FROM PLAYER Y
WHERE Y.PLAYER_ID = X.PLAYER_ID AND POSITION = 'MF')
ORDER BY 1, 2, 3, 4, 5;
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE TEAM_ID = 'K02'
AND PLAYER_ID NOT IN (SELECT PLAYER_ID FROM PLAYER
WHERE POSITION = 'MF')
ORDER BY 1, 2, 3, 4, 5;
교집합
다음은 교집합이다. 교집합은 INTERSECT로 표현 할 수 있다. 이는 논리연산자로 AND로 나타낼 수 있고, EXISTS 또는 IN 서브쿼리를 이용한 SQL 문으로 변경 가능하다.
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE TEAM_ID = 'K02'
INTERSECT
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE POSITION = 'GK'
ORDER BY 1, 2, 3, 4, 5;
------------------------
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE TEAM_ID = 'K02'
AND POSITION = 'GK'
ORDER BY 1, 2, 3, 4, 5;
------------------
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER X
WHERE X.TEAM_ID = 'K02'
AND EXISTS (SELECT 1 FROM PLAYER Y
WHERE Y.PLAYER_ID = X.PLAYER_ID
AND Y.POSITION = 'GK')
ORDER BY 1, 2, 3, 4, 5;
---------------------------
SELECT TEAM_ID 팀코드, PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM PLAYER
WHERE TEAM_ID = 'K02'
AND PLAYER_ID IN (SELECT PLAYER_ID FROM PLAYER
WHERE POSITION = 'GK')
ORDER BY 1, 2, 3, 4, 5;
'Computer Science > SQL-D' 카테고리의 다른 글
[SQLD] 24. 서브쿼리 (1) | 2016.09.01 |
---|---|
[SQLD] 23. 계층형 질의와 셀프 조인 (0) | 2016.09.01 |
[SQLD] 21. 표준 조인(STANDARD JOIN) (0) | 2016.08.24 |
[SQLD] 20. 조인(JOIN) (2) | 2016.08.18 |
[SQLD] 19. ORDER BY 절 (0) | 2016.08.18 |