일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- HTML
- 알고리즘
- python algorithm
- 코딩테스트
- VirtualBox
- sql
- dbeaver
- Python DataFrame
- 리눅스
- Oracle VM VirtualBox
- 파이썬 데이터프레임
- tibero
- Oracle
- Algorithm
- 파이썬
- PYTHON
- csharp
- 리눅스 명령어
- 오라클
- 파이썬 전처리
- Python 라이브러리
- it 용어
- C#
- 데이터베이스
- putty
- linux
- RFP
- it용어
- 파이썬 알고리즘
- MariaDB
- Today
- Total
오경석의 개발노트
Oracle_서브 쿼리(Subquery) 본문
서브 쿼리(Subquery) : 하나의 SQL 문 안에 포함돼 있는 또 다른 SQL 문. 메인 쿼리가 서브 쿼리를 포함하는 종속적인 관계.
서브 쿼리 | 사용 위치 | 설명 |
스칼라 서브 쿼리 | SELECT 절 | 단일 칼럼, 단일 행 반환(1개의 값) |
인라인 뷰 | FROM 절 | View와 사용적인 측면에서 동일(임시 뷰, 임시 테이블) |
중첩 서브 쿼리 | WHERE 절, HAVING 절 | 다중 칼럼 또는 다중 행 반환 |
서브쿼리 종류 | 설명 |
Un_correlated(비연관) 서브쿼리 | 서브쿼리가 메인쿼리 칼럼을 가지고 있지 않는 형태의 서브쿼리이다. 메인쿼리에 값(서브쿼리가 실행된 결과)을 제공하기 위한 목적으로 주로 사용 |
Correlated(연관) 서브쿼리 | 서브쿼리가 메인쿼리 칼럼을 가지고 있는 형태의 서브쿼리이다. 일반적으로 메인쿼리가 먼저 수행되어 읽혀진 데이터를 서브쿼리에서 조건이 맞는지 확인 하고자 할 때 주로 사용 |
□ 단일 행 서브 쿼리
서브 쿼리가 단일 행 비교 연산자(=, <, <=, >, >=, <>)와 함께 사용할 때는 서브쿼리의 결과 건수가 반드시 1건 이하이어야 한다. 만약, 서브쿼리의 결과 건수가 2건 이상을 반환하면 SQL문은 실행시간(Run Time) 오류가 발생한다. 이런 종류의 오류는 컴파일할 때(Compile Time)는 알 수 없는 오류이다. 단일 행 서브쿼리의 예로 '정남일' 선수가 소속된 팀의 선수들에 대한 정보를 표시하는 문제를 가지고 설명해 보면 다음과 같다.
[그림 Ⅱ-2-13]은 2개의 SQL문으로 구성되어 있다. 정남일 선수의 소속팀을 알아내는 SQL문(서브쿼리 부분)과 이 결과를 이용해서 해당 팀에 소속된 선수들의 정보를 출력하는 SQL문(메인쿼리 부분)으로 구성된다. 정남일 선수가 소속된 팀의 선수들에 대한 정보를 표시하는 문제를 서브쿼리 방식의 SQL문으로 작성하면 다음과 같다.
SELECT PLAYER_NAME AS 선수명,
POSITION AS 포지션,
BACK_NO AS 백넘버
FROM PLAYER
WHERE TEAM_ID = (SELECT TEAM_ID
FROM PLAYER
WHERE PLAYER_NAME = '정남일')
ORDER BY PLAYER_NAME;
정남일 선수의 소속팀을 알아내는 서브쿼리가 먼저 수행되어 정남일 선수의 소속팀 코드가 반환된다. 메인쿼리는서브쿼리에서 반환된 결과를 이용해서 조건을 만족하는 선수들의 정보를 출력한다.
만약, 정남일 선수가 동명이인이었다면 2건 이상의 결과가 반환되어 SQL문은 오류가 발생될 것이다. 테이블 전체에 하나의 그룹함수를 적용할 때는 그 결과값이 1건이 생성되기 때문에 단일 행 서브쿼리로서 사용 가능하다.
선수들 중에서 키가 평균 이하인 선수들의 정보를 출력하는 문제를 가지고 그룹함수를 사용한 서브쿼리를 알아보도록 한다.
[그림 Ⅱ-2-14]는 2개의 SQL문으로 구성되어 있다. 선수들의 평균키를 알아내는 SQL문(서브쿼리 부분)과 이 결과를 이용해서 키가 평균 이하의 선수들의 정보를 출력하는 SQL문(메인쿼리 부분)으로 구성된다. [그림 Ⅱ-2-14]를 SQL문으로 작성하면 다음과 같다.
SELECT PLAYER_NAME AS 선수명,
POSITION AS 포지션,
BACK_NO AS 백넘버
FROM PLAYER
WHERE HEIGHT <= (SELECT AVG(HEIGHT)
FROM PLAYER)
ORDER BY PLAYER_NAME;
□ 다중 행 서브 쿼리
서브 쿼리의 결과가 2건 이상 반환될 수 있다면 반드시 다중 행 비교 연산자(IN, ALL, ANY, SOME)와 함께 사용해야 한다. 그렇지 않으면 SQL문은 오류를 반환한다. 다중 행 비교 연산자는 다음과 같다.
선수들 중에서 ‘정현수’라는 선수가 소속되어 있는 팀 정보를 출력하는 서브쿼리를 작성하면 다음과 같다.
SELECT REGION_NAME AS 연고지명,
TEAM_NAME AS 팀명
FROM TEAM
WHERE TEAM_ID = (SELECT TEAM_ID
FROM PLAYER
WHERE PLAYER_NAME = '정현수')
ORDER BY TEAM_NAME;
위의 SQL문은 서브쿼리의 결과로 2개 이상의 행이 반환되어 단일 행 비교 연산자인 '='로는 처리가 불가능하기 때문에 에러가 반환되었다. 따라서 다중 행 비교 연산자로 바꾸어서 SQL문을 작성하면 다음과 같다.
SELECT REGION_NAME AS 연고지명,
TEAM_NAME AS 팀명
FROM TEAM
WHERE TEAM_ID IN (SELECT TEAM_ID
FROM PLAYER
WHERE PLAYER_NAME = '정현수')
ORDER BY TEAM_NAME;
실행 결과를 보면 '정현수'란 이름을 가진 선수가 두 명이 존재한다. 소속팀은 각각 전남 드래곤즈팀(K07)과 성남 일화천마팀(K08)이다. 본 예제에서는 동명이인에 대한 내용을 예로 들었지만, 서브쿼리의 실행 결과가 2건 이상이 나오는 모든 경우에 다중 행 비교 연산자를 사용해야 한다.
□ 다중 칼럼 서브 쿼리
다중 칼럼 서브쿼리는 서브쿼리의 결과로 여러 개의 칼럼이 반환되어 메인쿼리의 조건과 동시에 비교되는 것을 의미한다. 소속팀별 키가 가장 작은 사람들의 정보를 출력하는 문제를 가지고 다중 칼럼 서브쿼리를 알아보도록 한다. 소속팀별 키가 가장 작은 사람들의 정보는 GROUP BY를 이용하여 찾을 수 있으므로 다음과 같이 SQL문을 작성할 수 있다.
SELECT TEAM_ID AS 팀코드,
PLAYER_NAME AS 선수명,
POSITION AS 포지션,
BACK_NO AS 백넘버,
HEIGHT AS 키
FROM PLAYER
WHERE (TEAM_ID, HEIGHT) IN (SELECT TEAM_ID, MIN(HEIGHT)
FROM PLAYER
GROUP BY TEAM_ID)
ORDER BY TEAM_ID, PLAYER_NAME;
SQL문의 실행 결과를 보면 서브쿼리의 결과값으로 소속팀코드(TEAM_ID)와 소속팀별 가장 작은 키를 의미하는 MIN(HEIGHT)라는 두 개의 칼럼을 반환했다. 메인쿼리에서는 조건절에 TEAM_ID와 HEIGHT 칼럼을 괄호로 묶어서 서브쿼리 결과와 비교하여 원하는 결과를 얻었다.
실행 결과에서 보면 하나 팀에서 키가 제일 작은 선수 한 명씩만 반환된 것이 아니라 같은 팀에서 여러 명이 반환된 것을 확인할 수 있다. 이것은 동일 팀 내에서 조건(팀별 가장 작은 키)을 만족하는 선수가 여러 명이 존재하기 때문이다. 그러나 이 기능은 SQL Server에서는 지원되지 않는 기능이다.
□ 연관 서브 쿼리
연관 서브쿼리(Correlated Subquery)는 서브쿼리 내에 메인쿼리 칼럼이 사용된 서브쿼리이다. 선수 자신이 속한 팀의 평균 키보다 작은 선수들의 정보를 출력하는 SQL문을 연관 서브쿼리를 이용해서 작성해 보면 다음과 같다
SELECT T.TEAM_ID 팀명,
M.PLAYER_NAME 선수명,
M.POSITION 포지션,
M.BACK_NO 백넘버,
M.HEIGHT 키
FROM PLAYER M, TEAM T
WHERE M.HEIGHT < ( SELECT AVG(S.HEIGHT)
FROM PLAYER S
WHERE S.TEAM_ID = M.TEAM_ID
GROUP BY S.TEAM_ID )
AND T.TEAM_ID = M.TEAM_ID
ORDER BY 선수명;
○ EXIST
EXISTS 서브쿼리는 항상 연관 서브쿼리로 사용된다. 또한 EXISTS 서브쿼리의 특징은 아무리 조건을 만족하는 건이 여러 건이더라도 조건을 만족하는 1건만 찾으면 추가적인 검색을 진행하지 않는다. 다음은 EXISTS 서브쿼리를 사용하여 '20120501' 부터 '20120502' 사이에 경기가 있는 경기장을 조회하는 SQL문이다.
SELECT STADIUM_ID,
STADIUM_NAME
FROM STADIUM
WHERE EXISTS(SELECT 1 -- 컬럼은 불필요하지만 SELECT 절을 빈 값으로 둘 수 없어서 1을 기입, 출력은 되지 않음
FROM SCHEDULE
WHERE STADIUM.STADIUM_ID = SCHEDULE.STADIUM_ID
AND SCHEDULE.SCHE_DATE BETWEEN 20120501 AND 20120502); -- 만족하는 조건이 없을 경우 빈 값 출력
□ SELECT 절의 서브 쿼리
SELECT 절에서 사용하는 쿼리를 스칼라 서브 쿼리(Scalar Subquery)라고 한다. 스칼라 서브 쿼리는 한 행, 한 칼럼(1 Row 1 Column)만을 반환하는 서브 쿼리를 말한다. 스칼라 서브 쿼리는 칼럼을 쓸 수 있는 대부분의 곳에서 사용할 수 있다.
선수 정보와 해당 선수가 속한 팀의 평균키를 함께 출력하는 예제로 스칼라 서브 쿼리를 설명하면 다음과 같다.
[그림 Ⅱ-2-15]는 2개의 SQL문으로 구성되어 있다. 선수들의 정보를 출력하는 SQL문(메인쿼리 부분)과 해당 선수의 소속팀별 평균키를 알아내는 SQL문(서브쿼리 부분)으로 구성된다. 스칼라 서브쿼리 또한 단일 행 서브쿼리이기 때문에 결과가 2건 이상 반환되면 SQL문은 오류를 반환한다.
여기서 선수의 소속팀별 평균키를 알아내는 스칼라 서브쿼리는 메인쿼리의 결과 건수만큼 반복수행 된다. [그림 Ⅱ-2-15]를 SQL문으로 작성하면 다음과 같다.
SELECT PLAYER_NAME AS 선수명,
HEIGHT AS 키,
ROUND((SELECT AVG(HEIGHT)
FROM PLAYER X
WHERE X.TEAM_ID = P.TEAM_ID)) AS 팀평균키
FROM PLAYER P;
□ FROM 절에서 서브 쿼리
FROM 절에서 사용되는 서브쿼리를 인라인 뷰(Inline View)라고 한다. FROM 절에는 테이블 명이 오도록 되어 있다. 그런데 서브쿼리가 FROM 절에 사용되면 어떻게 될까? 서브쿼리의 결과가 마치 실행 시에 동적으로 생성된 테이블인 것처럼 사용할 수 있다. 인라인 뷰는 SQL문이 실행될 때만 임시적으로 생성되는 동적인 뷰이기 때문에 데이터베이스에 해당 정보가 저장되지 않는다. 그래서 일반적인 뷰를 정적 뷰(Static View)라고 하고 인라인 뷰를 동적 뷰(Dynamic View)라고도 한다.
인라인 뷰는 테이블 명이 올 수 있는 곳에서 사용할 수 있다. 서브쿼리의 칼럼은 메인쿼리에서 사용할 수 없다고 했다. 그러나 인라인 뷰는 동적으로 생성된 테이블이다. 인라인 뷰를 사용하는 것은 조인 방식을 사용하는 것과 같다. 그렇기 때문에 인라인 뷰의 칼럼은 SQL문 자유롭게 참조할 수 있다. K-리그 선수들 중에서 포지션이 미드필더(MF)인 선수들의 소속팀명 및 선수 정보를 출력하고자 한다. 인라인 뷰를 활용해서 SQL문을 만들어 보자.
SELECT T.TEAM_NAME AS 팀명,
P.PLAYER_NAME AS 선수명,
P.BACK_NO AS 백넘버
FROM (SELECT TEAM_ID,
PLAYER_NAME,
BACK_NO
FROM PLAYER
WHERE POSITION = 'MF') P, TEAM T
WHERE P.TEAM_ID = T.TEAM_ID
ORDER BY 선수명;
SQL문을 보면 선수들 중에서 포지션이 미드필더(MF) 선수들을 인라인 뷰를 통해서 추출하고 인라인 뷰의 결과와 TEAM 테이블과 조인해서 팀명(TEAM_NAME)을 출력하고 있다. 인라인 뷰에서는 ORDER BY절을 사용할 수 있다. 인라인 뷰에 먼저 정렬을 수행하고 정렬된 결과 중에서 일부 데이터를 추출하는 것을 TOP-N 쿼리라고 한다. TOP-N 쿼리를 수행하기 위해서는 정렬 작업과 정렬 결과 중에서 일부 데이터만을 추출할 수 있는 방법이 필요하다. Oracle에서는 ROWNUM이라는 연산자를 통해서 결과로 추출하고자 하는 데이터 건수를 제약할 수 있다.
SELECT PLAYER_NAME AS 선수명,
POSITION AS 포지션,
BACK_NO AS 백넘버,
HEIGHT AS 키
FROM (SELECT PLAYER_NAME,
POSITION,
BACK_NO,
HEIGHT
FROM PLAYER
WHERE HEIGHT IS NOT NULL
ORDER BY HEIGHT DESC)
WHERE ROWNUM <= 5;
인라인 뷰에서 선수의 키를 내림차순으로 정렬(가장 키가 큰 선수부터 출력)한 후 메인쿼리에서 ROWNUM을 사용해서 5명의 선수의 정보만을 추출하였다. 이것은 모든 선수들 중에서 가장 키가 큰 5명의 선수를 출력한 것이다. 만약, 다른 선수 중에서 키가 192인 선수가 더 존재하더라도 해당 SQL문에서는 데이터가 출력되지 않는다. 이런 데이터까지 추출하고자 한다면 분석함수의 RANK관련 함수를 사용해야 한다.
□ HAVING 절에서 서브 쿼리
HAVING 절은 그룹함수와 함께 사용될 때 그룹핑된 결과에 대해 부가적인 조건을 주기 위해서 사용한다. 평균키가 삼성 블루윙즈팀의 평균키보다 작은 팀의 이름과 해당 팀의 평균키를 구하는 SQL문을 작성하면 다음과 같다.
SELECT P.TEAM_ID AS 팀코드,
T.TEAM_NAME AS 팀명,
ROUND(AVG(P.HEIGHT)) AS 평균키
FROM PLAYER P, TEAM T
WHERE P.TEAM_ID = T.TEAM_ID
GROUP BY P.TEAM_ID, T.TEAM_NAME
HAVING AVG(P.HEIGHT) < (SELECT AVG(HEIGHT)
FROM PLAYER
WHERE TEAM_ID ='K02');
출처 : SQL 전문가 가이드
출처 : https://gent.tistory.com/464
[Oracle] 오라클 서브쿼리 종류 및 사용법 (SubQuery)
오라클에서 쿼리문을 작성하다 보면 서브 쿼리(subquery)를 자주 접하게 된다. 서브 쿼리를 처음 접하면 아주 복잡하게 느껴지는데, 기능을 조금만 익히면 쿼리문을 작성할 때 아주 유용하게 사용
gent.tistory.com
'프로그래밍 언어 > Oracle' 카테고리의 다른 글
Oracle_IN, EXIST, JOIN 개념 및 차이점 (0) | 2023.11.25 |
---|---|
Oracle_조인 (1) | 2023.11.15 |
Oracle_SELECT 문장 실행 순서 (1) | 2023.11.12 |
Oracle_ORDER BY 절 (0) | 2023.11.11 |
Oracle_GROUP BY, HAVING 절 (0) | 2023.11.10 |