메인쿼리의 칼럼을 사용할 수 있으며(메인쿼리에서는 서브쿼리의 칼럼 사용 불가), ORDER BY절은 사용할 수 없음
단일행 비교연산자를 사용할 경우 결과는 1건 이하, 2건 이상의 결과를 반환할때는 다중행 비교연산자를 사용
메인쿼리 칼럼 사용 여부에 따라 연관 서브쿼리, 비연관 서브쿼리로 구분
함수가 들어가는 위치에 따라 스칼라 서브쿼리, 인라인 뷰, 중첩 서브쿼리로 구분
스칼라 서브쿼리
SELECT문의 칼럼이 입력되는 위치(SELECT, ORDER BY 등)에 들어가는 서브쿼리
칼럼이 입력되는 위치에 삽입되므로 스칼라 서브쿼리의 결과는 하나의 칼럼만을 가져야 함
인라인 뷰
FROM절의 테이블이 입력되는 위치에 들어가는 서브쿼리
쿼리 실행 시 동적으로 생성되는 테이블
복잡한 쿼리를 단계적으로 작성할 수 있고 전체가 아닌 테이블의 일부분만 불러와 비교 횟수를 줄일 수 있음
SELECT절이 중첩되어 쿼리가 복잡해지지만 별명(Alias) 사용해 가독성을 높일 수 있음
중첩 서브쿼리
WHERE절과 HAVING절에 쿼리가 중첩되어 들어가는 서브쿼리
메인쿼리에서 참조하고 있는 테이블의 칼럼을 서브쿼리 내에서 다시 사용 가능
스칼라 서브쿼리나 인라인 뷰와 달리 반환하는 값의 형태가 하나가 아닌 다양한 값 반환 가능
단일행(Single Row): 1건 이하의 데이터를 반환, 단일행 비교연산자(=, >, < 등)의 연산 대상으로 사용
다중행(Multi Row): 2건 이상의 데이터를 반환, 다중행 비교연산자(IN, ALL, EXIST 등)의 연산 대상으로 사용
다중칼럼(Multi Column): 여러 칼럼의 값을 반환(벡터), 여러 칼럼을 가진 테이블 형태
뷰(View)
참조 시 동적으로 메모리 생성되는 임시 또는 가상 테이블
CREATE VIEW로 시작되는 DDL 코드로 생성 가능
복잡하고 긴 SELECT문을 만들어두고 사용할 수 있어 쿼리를 간결하고 단순하게 작성할 수 있지만 삽입, 수정, 삭제에 대한 제한이 있고 인덱스를 가질 수 없는 단점 존재
집합연산자
두 테이블에 대한 집합 연산(합집합, 교집합 등)을 수행하는 연산자
JOIN과 달리 특정한 기준키 없이 두 테이블의 레코드들에 대하여 연산을 수행하므로 두 테이블의 스키마가 동일해야 함
스키마 구성은 동일하나 칼럼의 이름은 다를 수 있는데, 반환되는 칼럼의 이름은 첫 번째 테이블을 따름
UNION ALL / UNION
두 테이블에 포함된 모든 레코드들을 모두 포함하는 합집합 연산을 수행
두 테이블에 모두에 포함된 공통의 레코드(합집합)는 중복 레코드라도 함
중복 레코드들을 중복된 개수만큼 그대로 포함시키는 것은 UNION ALL, 하나만 포함시키는 것은 UNION(한쪽 테이블 내에서의 중복까지 제거)
INTERSECT
두 테이블에 공통적으로 포함된 레코드만을 포함하는 교집합 연산을 수행
MINUS / EXCEPT
왼쪽 테이블에서 두 테이블에 공통적으로 포함된 레코드들을 제외시킨 결과를 반환하는 차집합 연산을 수행
왼쪽 UNION, 오른쪽 UNION ALL
왼쪽 INTERSECT, 오른쪽 MINUS / EXCEPT
그룹함수
GROUP BY절에 따른 결과에 대해서 그룹 별로 연산을 수행하는 함수
ROLLUP
GROUP BY절에 들어가는 칼럼을 대상으로 하위 그룸핑을 수행하는 함수
마지막 행(Row)에 전체를 하나로 묶은 그룹을 추가
SELECT
brand AS '브랜드',
SUM(sales_count) AS '판매수'
FROM car
GROUP BY ROLLUP(brand);
=> 브랜드 | 판매수
KG 모빌리티 | 290
기아 | 642
제네시스 | 725
현대 | 728
NULL | 2385
SELECT
brand AS '브랜드',
name AS '자동차명',
SUM(sales_count) AS '판매수'
FROM car
GROUP BY ROLLUP(brand, name);
=> 브랜드 | 자동차명 | 판매수
KG 모빌리티 | 렉스턴 스포츠 칸 | 140
KG 모빌리티 | 코란도 투리스모 | 150
KG 모빌리티 | NULL | 290
기아 | EV9 | 153
기아 | K9 | 259
기아 | 카니발 | 230
기아 | NULL | 642
제네시스 | G90 | 395
제네시스 | GV80 | 330
제네시스 | NULL | 725
현대 | 싼타페 | 296
현대 | 아이오닉N | 122
현대 | 팰리세이드 | 310
현대 | NULL | 728
NULL | NULL | 2385
CUBE
ROLLUP과 비슷하지만 1차적인 하위 그룹핑만 수행하는데 반하여 조합 가능한 모든 경우로 그룹핑을 수행
인자가 1개인 경우 ROLLUP과 동일, 두 개 이상인 경우 결과는 다름
SELECT
brand AS '브랜드',
name AS '자동차명',
SUM(sales_count) AS '판매수'
FROM car
GROUP BY CUBE(brand, name);
=> 브랜드 | 자동차명 | 판매수
KG 모빌리티 | 렉스턴 스포츠 칸 | 140
KG 모빌리티 | 코란도 투리스모 | 150
KG 모빌리티 | NULL | 290
기아 | EV9 | 153
기아 | K9 | 259
기아 | 카니발 | 230
기아 | NULL | 642
제네시스 | G90 | 395
제네시스 | GV80 | 330
제네시스 | NULL | 725
현대 | 싼타페 | 296
현대 | 아이오닉N | 122
현대 | 팰리세이드 | 310
현대 | NULL | 728
NULL | 렉스턴 스포츠 칸 | 140
NULL | 코란도 투리스모 | 150
NULL | EV9 | 153
NULL | K9 | 259
NULL | 카니발 | 230
NULL | G90 | 395
NULL | GV80 | 330
NULL | 싼타페 | 296
NULL | 아이오닉N | 122
NULL | 팰리세이드 | 310
NULL | NULL | 2385
GROUPING SETS
그룹핑할 대상을 지정하는 함수
ROLLUP, CUBE의 경우 소계, 총계 형태로 자동으로 그룹핑되지만 GROUPING SETS는 입력된 인자에 대해서만 소계를 구할 때 사용
인자에 ROLLUP이나 CUBE 함수를 넣을 수 있음
SELECT
brand AS '브랜드',
name AS '자동차명',
SUM(sales_count) AS '판매수'
FROM car
GROUP BY GROUPING SETS(brand, name);
=> 브랜드 | 자동차명 | 판매수
KG 모빌리티 | NULL | 290
기아 | NULL | 642
제네시스 | NULL | 725
현대 | NULL | 728
NULL | 렉스턴 스포츠 칸 | 140
NULL | 코란도 투리스모 | 150
NULL | EV9 | 153
NULL | K9 | 259
NULL | 카니발 | 230
NULL | G90 | 395
NULL | GV80 | 330
NULL | 싼타페 | 296
NULL | 아이오닉N | 122
NULL | 팰리세이드 | 310
GROUPING
ROLLUP, CUBE, GROUPING SETS과 함께 사용하여 소계에 해당하는 결과 행과 그렇지 않은 행을 구분
소계에 해당하는 결과 행에 경우 1, 그렇지 않은 경우 0을 반환, CASE문과 함께 사용하여 소계나 총계 표시
SELECT
CASE GROUPING(brand) WHEN 1 THEN '총계' ELSE brand END AS '브랜드',
SUM(sales_count) AS '판매수'
FROM car
GROUP BY ROLLUP(brand);
=> 브랜드 | 판매수
KG 모빌리티 | 290
기아 | 642
제네시스 | 725
현대 | 728
총계 | 2385
윈도우함수
행과 행 간의 관계를 나타내는 연산을 쉽게 하기 위한 함수
GROUP BY 연산과 비슷하지만 각 행을 대상으로 연산 수행 후 새로운 구성을 만드는 GROUP BY 연산과는 달리 새로운 행을 추가하거나 기존의 값을 변경
COUNT: 값이 NULL인 행을 제외한 행의 개수를 파티션 별로 집계하거나 누적 집계를 계산하여 반환
SUM: 입력된 칼럼에 대해 파티션 별 합계를 구하거나 누적 합계를 계산하여 반환
AVG: 입력된 칼럼에 대해 파티션 별 평균을 구하거나 누적 평균을 계산하여 반환
MIN: 입력된 칼럼에 대해 파티션 별 최솟값을 반환
MAX: 입력된 칼럼에 대해 파티션 별 최댓값을 반환
SELECT
DISTINCT brand AS '브랜드',
COUNT(*) OVER(PARTITION BY brand) AS '차량수',
SUM(price) OVER(PARTITION BY brand) AS '총 가격',
AVG(price) OVER(PARTITION BY brand) AS '평균 가격',
MIN(price) OVER(PARTITION BY brand) AS '최소 가격',
MAX(price) OVER(PARTITION BY brand) AS '최대 가격'
FROM car;
=> 브랜드 | 차량수 | 총 가격 | 평균 가격 | 최소 가격 | 최대 가격
KG 모빌리티 | 2 | 79690000 | 39845000.0000 | 35000000 | 44690000
기아 | 3 | 253410000 | 84470000.0000 | 77440000 | 92000000
제네시스 | 2 | 182140000 | 91070000.0000 | 78300000 | 103840000
현대 | 3 | 171000000 | 57000000.0000 | 45000000 | 76000000
행 순서함수
FIRST_VALUE: 파티션 별로 그룹핑하여 가장 처음 값 반환
LAST_VALUE: 파티션 별로 그룹핑하여 가장 마지막 값 반환
LAG: 입력된 인자의 값만큼 이전 행의 값을 반환
LEAD: 입력된 인자의 값만큼 이후 행의 값을 반환
SELECT
DISTINCT brand AS '브랜드',
FIRST_VALUE(price) OVER(PARTITION BY brand ORDER BY price DESC) AS 'FIRST_VALUE'
FROM car;
=> 브랜드 | FIRST_VALUE
KG 모빌리티 | 44690000
기아 | 92000000
제네시스 | 103840000
현대 | 76000000
SELECT
brand AS '브랜드',
name AS '자동차명',
price AS '가격',
LAG(name, 2) OVER(ORDER BY price DESC) AS 'LAG',
LEAD (name, 2) OVER(ORDER BY price DESC) AS 'LEAD'
FROM car;
=> 브랜드 | 자동차명 | 가격 | LAG | LEAD
제네시스 | G90 | 103840000 | NULL | EV9
기아 | 카니발 | 92000000 | NULL | GV80
기아 | EV9 | 83970000 | G90 | K9
제네시스 | GV80 | 78300000 | 카니발 | 아이오닉N
기아 | K9 | 77440000 | EV9 | 팰리세이드
현대 | 아이오닉N | 76000000 | GV80 | 싼타페
현대 | 팰리세이드 | 50000000 | K9 | 렉스턴 스포츠 칸
현대 | 싼타페 | 45000000 | 아이오닉N | 코란도 투리스모
KG 모빌리티 | 렉스턴 스포츠 칸 | 44690000 | 팰리세이드 | NULL
KG 모빌리티 | 코란도 투리스모 | 35000000 | 싼타페 | NULL
비율함수
파티션 별로 전체 개수나 합계를 구한 후 그에 대한 비율을 구하는 함수
CUME_DIST: 파티션 별로 전체 개수에 대한 누적 백분율을 소수점 단위로 계산하여 반환, 마지막 행이 1
PERCENT_RANK: 파티션 별로 순서별 백분율을 반환, 가장 첫 행이 0, 마지막 행이 1
NTILE: 파티션을 N등분하여 1부터 N까지의 등급 값을 반환
RATIO_TO_REPORT: 파티션 별 합계에 대한 비율을 계산하여 반환
SELECT
brand AS '브랜드',
name AS '자동차명',
price AS '가격',
CUME_DIST() OVER(ORDER BY price) AS 'CUME_DIST',
PERCENT_RANK() OVER(ORDER BY price) AS 'PERCENT_RANK',
NTILE(5) OVER(ORDER BY price) AS 'NTILE',
RATIO_TO_REPORT(price) OVER(PARTITION BY brand) AS 'RATIO_TO_REPORT'
FROM car
ORDER BY price DESC;
=> 브랜드 | 자동차명 | 가격 | CUME_DIST | PERCENT_RANK | NTILE | RATIO_TO_REPORT
제네시스 | G90 | 103840000 | 1.0 | 1.0 | 5 | 0.151
기아 | 카니발 | 92000000 | 0.9 | 0.8888888888888888 | 5 | 0.134
기아 | EV9 | 83970000 | 0.8 | 0.7777777777777778 | 4 | 0.122
제네시스 | GV80 | 78300000 | 0.7 | 0.6666666666666666 | 4 | 0.114
기아 | K9 | 77440000 | 0.6 | 0.5555555555555556 | 3 | 0.113
현대 | 아이오닉N | 76000000 | 0.5 | 0.4444444444444444 | 3 | 0.111
현대 | 팰리세이드 | 50000000 | 0.4 | 0.3333333333333333 | 2 | 0.073
현대 | 싼타페 | 45000000 | 0.3 | 0.2222222222222222 | 2 | 0.066
KG 모빌리티 | 렉스턴 스포츠 칸 | 44690000 | 0.2 | 0.1111111111111111 | 1 | 0.065
KG 모빌리티 | 코란도 투리스모 | 35000000 | 0.1 | 0.0 | 1 | 0.051
Top N 쿼리
멜론 차트 100과 같이 상위 N 순위까지 추출하는 쿼리
ROWNUM 함수
현재 저장된 데이터를 그대로 두고 각 행에 순차적인 번호를 붙여주는 함수
테이블의 첫 행부터 차례로 순회하면서 값을 반환하기 때문에 중간을 건너뛰고 값을 가져올 수 없음
WHERE 조건이 FALSE가 되면 순회를 멈추고 결과를 반환(<, <= 비교연산자만 WHERE 조건으로 사용)
ORDER BY와 함께 사용할 경우 행에 번호가 매겨지고 나서 정렬되므로 출력되는 번호가 뒤죽박죽 될 수 있어 주의해야 함
SELECT
ROWNUM
student_name AS '학생명',
test_score AS '시험점수'
FROM test_result WHERE ROWNUM <= 5
=> ROWNUM | 학생명 | 시험점수
1 | 박철수 | 94
2 | 김수현 | 91
3 | 박지훈 | 91
4 | 이상진 | 84
5 | 박정민 | 84
SELECT
ROWNUM
student_name AS '학생명',
test_score AS '시험점수'
FROM test_result WHERE ROWNUM = 5
=> ROWNUM | 학생명 | 시험점수
# 대소비교가 아닌 등식비교를 할 경우
# ROWNUM이 1일 때 조건이 FALSE가 되어 더 이상 순회되지 않아 아무런 결과가 나오지 않음
윈도우함수의 순위함수
RANK, DENSE_RANK, ROW_NUMBER 함수를 사용해서 Top N 쿼리 작성 가능
SELECT * FROM (
SELECT
student_name AS '학생명',
test_score AS '시험점수',
RANK() OVER(ORDER BY test_score DESC) AS 'RANK',
DENSE_RANK() OVER(ORDER BY test_score DESC) AS 'DENSE_RANK',
ROW_NUMBER() OVER(ORDER BY test_score DESC) AS 'ROW_NUMBER'
FROM test_result
) AS TR WHERE TR.ROW_NUMBER <= 5;
=> 학생명 | 시험점수 | RANK | DENSE_RANK | ROW_NUMBER
박철수 | 94 | 1 | 1 | 1
김수현 | 91 | 2 | 2 | 2
박지훈 | 91 | 2 | 2 | 3
이상진 | 84 | 4 | 3 | 4
박정민 | 84 | 4 | 3 | 5
계층형 질의와 셀프 조인
계층형 질의
계층형 데이터 모델을 다룰 때 상위(부모) 노드에서 하위(자식) 노드로 연쇄적으로 데이터에 접근해서 결과를 조회해야 할 경우 사용