본문 바로가기

데이터 사이언스/SQL

[SQLD 학습 자료 요약] SQL 기본 및 활용 2.2. 집합 연산자

본 문서의 내용은 한국데이터산업진흥원에서 펴낸 SQL 전문가 가이드를 기반으로 자격증 취득에 도움이 될 개념을 정리한 것입니다.

SQL 전문가 가이드
국내도서
저자 : 한국데이터산업진흥원
출판 : 한국데이터산업진흥원 2020.05.29
상세보기

 

2. 집합 연산자

집합 연산자는 여러 개의 질의의 결과를 연결하여 하나로 결합하는 방식을 사용한다. 즉, 집합 연산자는 2 개 이상의 질의 결과를 하나의 결과로 만들어 준다. 일반적으로 집합 연산자를 사용하는 상황은 서로 다른 테이블에서 유사한 형태의 결과를 반환하는 것을 하나의 결과로 합치고자 할 때와 동일 테이블에서 서로 다른 질의를 수행하여 결과를 합치고자 할 때 사용할 수 있다.

집합 연산자 도식화 (출처: SQL 전문가 가이드)

 

집합 연산자의 종류

1. UNION

  • 모든 중복된 행은 하나의 행으로 만들어서 합집합 결과를 반환한다.

 

2. UNION ALL

  • 중복된 행도 그대로 포함하여 합집합 결과를 반환한다. 즉, 단순히 결과만 합쳐놓은 것이다. 일반적으로 여러 질의 결과가 상호 배타적일 때 많이 사용한다.

 

3. INTERSECT

  • 결과에 대한 교집합으로 중복된 행은 하나로 만든다.

 

4. EXCEPT

  • 차집합 결과로 중복된 행은 하나의 행으로 만든다. (일부 데이터베이스는 MINUS를 사용함)

 

집합 연산자는 사용상의 제약조건을 만족한다면 어떤 형태의 SELECT 문이라도 이용할 수 있다. 집합 연산자는 여러 개의 SELECT 문을 연결하는 것에 지나지 않는다. ORDER BY 는 집합 연산을 적용한 최종 결과에 대한 정렬 처리이므로 가장 마지막 줄에 한번만 기술한다.

 

1) K-리그 소속 선수들 중에서 소속이 삼성블루윙즈팀인 선수들과 전남드래곤즈팀인 선수들에 대한 내용을 모두 보고 싶다.

SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE TEAM_ID = 'K02'  -- K02 = 수원삼성블루윙즈 TEAM_ID
UNION
SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE TEAM_ID = 'K07'  -- K07 = 전남드래곤즈 TEAM_ID

 

 

WHERE 절에 IN 또는 OR 연산자로도 변환이 가능하다. 다만 IN 또는 OR 연산자를 사용할 경우에는 결과의 표시 순서가 달라질 수 있다. 집합이라는 관점에서는 결과가 표시되는 순서가 틀렸다고 두 집합이 서로 다르다고 말할 수 없다. 만약, 결과의 동일한 표시 순서를 원한다면 ORDER BY 절을 사용해서 명시적으로 정렬 순서를 정의하는 것이 바람직하다.

-- OR 연산자 활용
SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE TEAM_ID = 'K02'
   OR TEAM_ID = 'K07';

-- IN 연산자 활용
SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE TEAM_ID IN ('K02', 'K07');

 

 

2) K-리그 소속 선수들에 대한 정보 중에서 포지션별 평균키와 팀별 평균키를 알고 싶다.

SELECT 'P' 구분코드, POSITION 포지션, ROUND(AVG(HEIGHT), 2) 평균키
FROM PLAYER
GROUP BY POSITION
UNION
SELECT 'T' 구분코드, TEAM_ID 팀명, ROUND(AVG(HEIGHT), 2) 평균키
FROM PLAYER
GROUP BY TEAM_ID
ORDER BY 1;

 

 

그룹함수도 집합 연산자에서 사용이 가능하다는 것을 알 수 있다. 또한 실제로 테이블에는 존재하지 않지만 결과 행을 구분하기 위해 SELECT 절에 칼럼('구분코드')을 추가할 수 있다는 것을 알 수 있다. 이와 같이 목적을 위해 SELECT 절에 임의의 칼럼을 추가하는 것은 다른 모든 SQL 문에서 적용 가능하다.

 

집합 연산자의 결과를 표시할 때 HEADING 부분은 첫 번째 SQL 문에서 사용된 HEADING 이 적용된다는 것을 알 수 있다. SQL 문에서 첫 번째 SELECT 절에서는 '포지션' HEADING 을 사용하였고 두 번째 SELECT 절에서는 '팀명' HEADING 을 사용하였다. 그러나 결과에는 '포지션' HEADING 으로 표시되었다.

첫 번째 SQL 문의 HEADING 적용

 

3) K-리그 소속 선수를 중에서 소속이 삼성블루윙즈팀이면서 포지션이 미드필더(MF)가 아닌 선수들의 정보를 보고 싶다.

-- Oracle
SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE TEAM_ID = 'K02'
MINUS   -- SQL Server에서는 EXCEPT 사용
SELECT TEAM_ID     팀코드,
       PLAYER_NAME 선수명,
       POSITION    포지션,
       BACK_NO     백넘버,
       HEIGHT      키
FROM PLAYER
WHERE POSITION = 'MF'
ORDER BY 1, 2, 3, 4, 5;

 

 

EXCEPT 연산자를 사용하지 않고 논리 연산자를 이용하여 동일한 결과의 SQL 문을 작성할 수 있다.

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;

 

 

MINUS 연산자는 NOT EXISTS 또는 NOT IN 서브쿼리를 이용한 SQL 문으로도 변경 가능하다.

-- NOT EXISTS 사용
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;

-- NOT IN 사용
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;

 

 

4) K-리그 소속 선수들 중에서 소속이 삼성블루윙즈팀이면서 포지션이 골키퍼(GK)인 선수들의 정보를 보고 싶다.

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;

 

 

다음과 같이 INTERSECT 연산자를 사용하지 않고도 논리 연산자만으로 결과가 동일한 SQL 문을 작성할 수 있다.

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;

 

 

INTERSECT 연산자는 EXISTS 또는 IN 서브쿼리를 이용한 SQL 문으로 변경 가능하다.

-- EXISTS 사용
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;

-- IN 사용
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;

 


↓SQL 전문가 가이드 요약 목록

더보기

 

따로 PDF 파일이 필요하신 분은 댓글을 통해 메일 주소 적어주시기 바랍니다.