SQL 기본문법 > 서브쿼리(SubQuery) - 단일행/복수행 서브쿼리

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


서브쿼리(SubQuery)

SQL문 내부에서 사용하는 SELECT문

서브쿼리는 WHERE, SELECT, FROM, HAVING절 안에 넣어 사용할 수 있고, 이러한 서브쿼리는 괄호안에 적어주어야 한다. 일반적으로 서브쿼리가 먼저 실행된 후 메인쿼리가 실행되는 순서로 작업이 이루어진다.

서브쿼리와 조인의 차이점

  • 데이터 추출 접근 방식이 다름
    • 서브쿼리는 한문장 안에 1개의 메인쿼리와 1개 이상의 서브쿼리가 있는 구조
    • 조인은 1개의 쿼리 안에 2개 이상의 테이블을 연결한 후 컬럼을 조회

간단한 쿼리 혹은 간단한 필터링 작업에는 서브쿼리가 좋겠지만, 쿼리가 복잡하거나 여러테이블간의 관계를 처리해야할 경우에는 조인을 사용하는 것이 더 좋다.

이러한 서브쿼리의 종류는 크게 두개로 구분된다.

  1. 단일 행 서브쿼리: 서브쿼리의 결과가 단일행이 나오는 쿼리
  2. 복수 행 서브쿼리: 서브쿼리의 결과가 어러행이 나오는 쿼리

단일 행 서브쿼리

서브쿼리의 결과가 단일행이 나오는 쿼리

=, >, <, >=, <= 같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.

-- 문제: 최고 마일리지를 보유한 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
WHERE 마일리지 = (SELECT MAX(마일리지) FROM 고객);

-- 사실 위에 문제는 아래와같이도 풀 수 있다.
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
ORDER BY 마일리지 desc LIMIT 1;

문제를 더 살펴보자.

-- 문제1: 주문번호 'H0250'을 주문한 고객에 대해 고객회사명과 담당자명을 보이시오
SELECT 고객회사명, 담당자명 FROM 고객
WHERE 고객번호 = (SELECT 고객번호 FROM 주문 WHERE 주문번호 = 'H0250');

-- 이걸 조인으로 풀어보자
SELECT 고객회사명, 담당자명 FROM 고객 
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호
WHERE 주문번호 = 'H0250';

-- 문제2: 부산광역시 고객의 최소 마일리지보다 더 큰 마일리지를 가진 고객 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객 
WHERE 마일리지 > (SELECT MIN(마일리지) FROM 고객 WHERE 도시 = '부산광역시');

이렇듯 비교연산자들을 통해서도 서브쿼리 구문을 만들어줄 수 있다.

복수 행 서브쿼리

서브쿼리의 결과가 여러행이 나오는 쿼리

IN, ALL, ANY, SOME, EXISTS 같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.

  • IN: 메인쿼리의 비교조건이 서브쿼리 결과 중 하나라도 일치하면 참(= 연산자로 비교)
  • ALL: 서브쿼리의 각 결과값을 비교연산자로 비교해서 모두 일치해야 참(>, <, >=, <=)
  • ANY, SOME: 서브쿼리의 각 결과값을 비교연산자로 비교해서 하나라도 일치하면 참(>, <, >=, <=)
  • EXISTS: 서브쿼리에 비교조건을 만족하는 결과가 존재하면 참 (행의 존재여부로 비교)
-- 문제1: 부산광역시 고객이 주문한 주문건수를 보이시오
SELECT COUNT(*) 주문건수 FROM 주문 
WHERE  고객번호 IN (SELECT 고객번호 FROM 고객 WHERE 도시 = '부산광역시');

-- 문제2: 부산광역시 고객의 마일리지들보다 마일리지가 큰 고객이 한명이라도 있는 정보를 보이시오 
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ANY(SELECT 마일리지 FROM 고객 WHERE 도시 = '부산광역시');

-- 문제3: 각 지역의 어느 평균 마일리지보다도 마일리지가 큰 고객의 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ALL (SELECT AVG(마일리지) FROM 고객 GROUP BY 지역);

-- 문제4: 한번이라도 주문한 적 있는 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명 FROM 고객
WHERE EXISTS (SELECT 고객번호 FROM 주문);

-- 위 문제를 IN을 사용해본다면?
SELECT 고객번호, 고객회사명 FROM 고객
WHERE 고객번호 IN (SELECT 고객번호 FROM 주문);

-- 위 문제를 JOIN을 사용해본다면?
SELECT DISTINCT 고객.고객번호, 고객회사명 FROM 고객
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호 ORDER BY 1

INNER JOIN의 경우 교집합만 가져오다보니, 주문을 한 고객번호만 조회될 것이다. 그렇기 때문에 특별한 조건문이 더이상 필요하지는 않다. 고객번호순으로 정렬한 뒤, 번호와 회사명은 여러개 보여줄 필요가 없으니 중복을 제거해주는 DISTINCT를 사용해준다.