멋쟁이v의 개발일지

[DB] 서브쿼리(스칼라, 인라인뷰, 중첩질의), UNION 본문

0년차/DB

[DB] 서브쿼리(스칼라, 인라인뷰, 중첩질의), UNION

멋쟁이v 2023. 6. 6. 19:05
728x90
320x100

[목차]


01. 서브쿼리

💡
쿼리 안에 들어가는 다른 쿼리

대표적으로 SELECT, FROM, WHERE 절에서 사용 가능하다.

서브 쿼리(부속 질의) 밖에 있는 쿼리 → 메인 쿼리 또는 주 질의

일반적으로 대량의 데이터를 다루는 경우, 데이터를 모두 합쳐서 연산하는 JOIN보다 필요한 데이터만 찾아서 조회하는 서브 쿼리가 성능이 좋다.

➡️ 서브 쿼리가 단일 행 반환 또는 다중 행을 반환에 따라서 분류 할 수 있다.

  • 단일 행 부속 질의
    • 서브 쿼리에서 하나의 행을 반환해서 메인 쿼리에 전달한다.
    • 스칼라 서브 쿼리, 비교 연산자에서 주로 나타난다.
  • 다중 행 서브 쿼리
    • 서브 쿼리 결과가 여러 개의 행을 반환하는 경우
    • IN연산자를 사용하여 여러 행을 처리할 수 있다.
  • SELECT절에 사용하는 스칼라 서브쿼리
    👉🏻
    SELECT절에 사용할 수 있다.

    단일 행과 단일 열의 결괏값을 반환한다.

    결괏값이 다중 행 또는 다중 열이면 에러 발생

    결과가 없다면 NULL을 출력한다.

    ➡️ 다중 행 또는 다중 열이라면, DBMS가 어떤 값을 출력해야 하는지 알 수 없다. (하나의 값만 출력해야 하는데, 조회 값이 여러 개.)

    ➡️ 예제

    구매자(고객) 별 총 구매 금액과 고객의 이름을 출력하시오.

    SELECT
    	-- 스칼라 서브 쿼리
    	(SELECT
    			m.m_name
    		FROM
    			tb_member AS m
    		WHERE
    			-- 밑에서 사용한 order 테이블의 alias를 쓸 수 있다.
    			m.m_id = o.o_id) AS '고객이름',
    	SUM(o.o_amount * g.g_price) AS '총 구매 금액'
    FROM
    	-- 총 구매 금액을 알기 위해 상품테이블과 주문테이블을 조인
    	tb_goods AS g
    	INNER JOIN
    	tb_order AS o
    	ON
    	g.g_code = o.o_g_code
    -- 구매자(고객) 별로 그룹을 묶는다.
    GROUP BY
    	o.o_id;

  • FROM절에 사용하는 인라인 뷰
    👉🏻
    FROM절에 사용하는 서브 쿼리를 말한다.

    인라인 뷰 라는 이름은 결과를 뷰 형태로 반환하기 때문이다.

    • 뷰 : 임시로 만들어진 가상 테이블을 말한다.

    FROM절에서 사용한 서브 쿼리의 결과를 하나의 테이블로 사용한다고 생각하면 된다.

    인라인 뷰를 사용하면 일반적인 테이블처럼 사용 가능하다.

    반환되는 값은 다중 열, 다중 행 전부 가능하다.

    ➡️ 기본 구문

    SELECT

    컬럼명

    FROM

    (SELECT * FROM 테이블명)

    JOIN, WHERE 등 사용 가능

    ➡️ 예제

    회원 레벨이 2인 회원들 중, 판매하는 상품 가격이 60만원 이상인 판매자의 ID/이름, 상품 이름, 각 상품의 가격을 조회한 후, 내림차순 정렬

    SELECT
    	-- 컬럼 조회
    	g.g_seller_id AS '판매자ID',
    	m.m_name AS '판매자이름',
    	g.g_name AS '상품이름',
    	g.g_price AS '상품가격'
    FROM
    	-- 인라인 뷰와 상품 테이블을 조인
    	(SELECT * FROM tb_member WHERE m_level = 2) AS m
    	INNER JOIN
    	tb_goods AS g
    	m.m_id = g.g_seller_id
    WHERE
    	-- 조건
    	g.g_price >= 600000
    -- 가격을 내림차순 정렬
    ORDER BY g.g_price DESC;

  • WHERE절에 사용하는 중첩 질의
    👉🏻
    WHERE절에 사용할 수 있다.

    IN연산자, 비교 연산자를 사용할 수 있다.

    ➡️ 다중 행 연산자 (IN, ANY 많이 사용)

    👉🏻
    IN
    • 리턴되는 값 중에서 조건에 해당하는 값이 있으면 참.
    • WHERE 컬럼 = 값;

      ➡️ 이퀄 기호를 사용하면 반환되는 값이 단일 행을 반환하는 경우에만 사용 가능하다.

      ➡️ IN연산자를 사용하면 반환 값이 여러 개 있을 경우, 다중 행을 반환할 수 있다.

    ANY, SOME(=OR)

    • 서브 쿼리에 의해 리턴되는 각각의 값과 조건을 비교하여 하나 이상을 만족하면 참이다.

    ALL(AND)

    • 서브 쿼리에 의해 리턴되는 각각의 값과 조건을 비교하여 모든 값을 만족하면 참이다.

    EXISTS, NOT EXISTS

    • 데이터의 존재 유무를 확인한다.
    • 메인 쿼리의 비교 조건이 서브 쿼리의 결과 중에서 만족하는 값이 하나라도 존재하면 참이다.

    ➡️ 예제

    1. goods table에서 각 제품의 평균 가격을 구하시오.
    1. 평균 가격보다 비싼 가격의 제품 이름과 가격을 조회하시오.
    -- 1번 문제
    SELECT
    	-- 상품 가격의 평균에 반올림한 값의 컬럼을 조회
    	ROUND(AVG(g.g_price), 0)
    FROM
    	tb_goods AS g;
    
    -- 2번문제
    SELECT
    	-- 제품 이름, 가격을 조회
    	g.g_name,
    	g.g_price
    FROM
    	tb_goods AS g
    WHERE
    	-- 중첩 질의
    	g.g_price > (SELECT
    									ROUND(AVG(g.g_price), 0)
    								FROM
    									tb_goods AS g;

    -- ANY 연산자 (=SOME, OR)
    SELECT
    	*
    FROM
    	tb_member AS m
    WHERE
    	-- level 테이블에서 레벨 번호가 1보다 큰 레벨 번호를 조회하고,
    	-- member테이블의 레벨에서 그 레벨 번호보다 큰 레벨 
    	m.m_level > ANY(SELECT
    										l.level_num
    									FROM
    										tb_member_level AS l
    									WHERE
    										l.level_num > 1);
    
    -- ALL 연산자 (=AND)
    SELECT
    *
    FROM
    	tb_member AS m
    WHERE
    	m.m_level > ALL(SELECT
    											l.level_num
    										FROM
    											tb_member_level AS l
    										WHERE
    											l.level_num > 1);
    
    -- EXISTS
    SELECT
    	*
    FROM
    	tb_member AS m
    WHERE
    	EXISTS (SELECT
    						*
    					FROM
    						tb_order AS o
    					WHERE
    						m.m_id = o.o_id);

    ➡️ 주의사항

    • 메인 쿼리의 WHERE절에서 비교하는 컬럼의 수와 서브 쿼리에서 조회하는 컬럼의 수 불일치로 발생하는 에러.
    -- 다중 행을 반환할 때, IN연산자 사용
    SELECT
    	*
    FROM
    	city AS c
    WHERE
    	-- 비교하는 컬럼의 수가 같아야 한다. (2개)
    	(c.CountryCode, c.Name) IN (SELECT
    											c1.CountryCode,
    											c1.Name
    										FROM
    											city AS c1
    										WHERE
    											c1.CountryCode = 'KOR' 
    											OR 
    											c1.CountryCode = 'JPN');

  • 쿼리 실행 순서
    👉🏻
    서브 쿼리가 먼저 실행되고, 메인 쿼리가 실행된다.

    서브 쿼리의 값이 먼저 반환되고, 메인 쿼리에서 비교한다.

    중첩 질의는 메인 쿼리가 실행되고, 서브쿼리가 실행된다.

  • 주의사항
    👉🏻
    서브 쿼리는 반드시 () 괄호 안에 작성해야 한다.

02. UNION

💡
여러 쿼리의 합집합

여러 SQL문을 합쳐서 하나의 SQL문으로 만드는 방법

예전 테이블의 데이터를 새로 만든 테이블과 함께 추출해야 하는 업무

  • 종류

    ➡️ UNION(기본값)

    👉🏻
    중복 결과를 없애고 데이터를 출력한다.

    중복된 행은 한 번만 출력한다.

    ➡️ UNION ALL

    👉🏻
    중복 데이터까지 포함해서 보여준다.

    중복된 행도 그대로 출력한다.

  • 특징

    ➡️ 실행 속도

    • UNION ALL 이 더 빠르다. → 중복 상관없이 전부 출력

    ➡️ 규칙

    👉🏻
    컬럼의 개수가 동일해야 한다.

    컬럼의 이름을 동일하게 한다. (다를 경우 Alias 사용)

    컬럼의 데이터 타입이 동일해야 한다.

    ➡️ 중요

    • UNION 사용 규칙이 DBMS마다 허용하는 범위 차이가 있다.
    • 하지만 규칙을 지켜서 작성하는 것이 좋다.


tag : #데이터베이스 #DB #서브쿼리 #스칼라 #인라인뷰 #중첩질의 #UNION


Uploaded by N2T

728x90
320x100
Comments