Notice
Recent Posts
Recent Comments
Link
«   2025/06   »
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
Tags
more
Archives
Today
Total
관리 메뉴

어리바리 신입 개발자의 얼렁뚱땅 개발 기록 ✨

23.05.29 / 서브 쿼리 본문

Database/MYSQL

23.05.29 / 서브 쿼리

낫쏘링 2023. 5. 29. 23:15
728x90

[ 메인 쿼리 ] 

  • 서브 쿼리의 밖에 있는 쿼리  (주 질의 / 외부 질의)
  • 메인 쿼리 안에 서브 쿼리를 작성한다.
  • 작성 순서 : SELECT > FROM > WHERE > GROUP BY > HAVING > ORDER BY
  • 실행 순서 : FROM > WHERE > GROUP BY > HAVING > SELECT > ORDER BY

[ 서브 쿼리 ]

  • 하나의 쿼리 안에 들어가는 다른 쿼리 (부속 질의 / 내부 질의)
  • 대표적으로 SELECT, FROM, WHERE 절에서 사용 가능하다.
  • 서브 쿼리 : 필요한 데이터만 찾아서 조회 / JOIN : 데이터를 모두 합쳐서 연산 -> 데이터가  대량일 경우 JOIN보다 서브 쿼리의 성능이 좋다.
[[ 종류 ]]
[ 스칼라 서브 쿼리 (SELECT절) ]
- 단일 행과 단일 열의 결괏값을 반환
- 결괏값이 다중 행 또는 다중 열이면 에러 발생
  다중 행 또는 다중 열일 때 DBMS가 어떤 값을 출력해야 하는 지 알 수 없음  -> 하나의 값만 출력해야하는데 조회 값이 여러 개가 된다.
  스칼라 서브 쿼리는 데이터가 많으면 반복적으로 SLELCT 수행하기 때문에 성능 저하 문제가 발생
- 결과가 없으면 NULL 출력
SELECT
	(SELECT 
		m.m_name -- *로 전체 컬럼 조회하면 다중 열 조회됨 -> 오류 발생 / 하나의 열만 선택
	FROM
		tb_member AS m
	WHERE
		m.m_id = o.o_id) AS '구매자 이름', 
    -- m 테이블의 튜플 중 o 테이블의 id와 일치하는 튜플의 m.m_name 컬럼을 구매자 이름이라는 컬럼으로 조회 
	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)구문이 들어가 하나의 가상테이블을 생성한다.
SELECT
      가상_테이블_별칭.조회할_컬럼
 FROM
      (SELECT * FROM 테이블_이름 AS 원래_테이블_별칭) AS 가상_테이블_별칭
SELECT
      가상_테이블_별칭.조회할_컬럼
 FROM
      (SELECT * FROM 테이블_이름1 AS 원래_테이블_별칭
       WHERE 조건) AS 가상_테이블_별칭
      INNER JOIN
      테이블_이름2 AS 테이블_별칭
      ON 가상_테이블_별칭.기준_컬럼 = 테이블_별칭.기준_컬럼 
-- 회원레벨이 2인 회원들 중
-- 판매하는 상품가격이 60만원이상인 판매자의 
-- ID,상품이름,회원레벨,각 상품의 가격을 조회한 후
-- 내림차순 정렬

SELECT
	g.g_seller_id,
	g.g_name,
	l.m_level,
	g.g_price
FROM  -- 인라인 뷰 : 메인 쿼리의 FROM에 작성!
	(SELECT   -- 기존 테이블에서 레벨이 2인 회원의  아이디와 레벨만 가져온 가상 테이블 l 생성
		m.m_id,
		m.m_level
	FROM
		tb_member AS m
	WHERE
		m.m_level = 2) AS l
	INNER JOIN	-- 상품 테이블 g와 가상테이블 l 조인 기준은 id (null 값 제외)
		tb_goods AS g
	ON
		l.m_id=g.g_seller_id
    -- 여기까지 FROM문을 통해 
    -- 레벨이 2인 회원의 정보 중 
    -- 아이디 컬럼, 레벨 컬럼, 상품 테이블의 컬럼(상품 이름,가격)을 가진 테이블이 생성된다.
WHERE 
	g.g_price >= 600000
ORDER BY g.g_price DESC; -- 서브 쿼리에 ORDER BY 쓸 수 없다더니? -> 서브 쿼리에 쓰인 것 아님


[ 중첩 질의 (WHERE절) ]
- 메인 쿼리의 WHERE 절의 단일 행 비교 연산자 또는 컬럼 뒤에 사용 가능
- 메인 쿼리의 WHERE 절에서는 =, >, < 등.. 과 같은 단일 행 비교 연산자만 사용 가능하다.
-- tb_goods 테이블에서
-- 각 제품의 평균 가격을 구해서
-- 평균 가격보다 비싼 가격의
-- 제품의 이름과 가격을 조회

SELECT
	g.g_name,
	g.g_price
FROM
	tb_goods AS g
WHERE
	g.g_price > (SELECT
						ROUND(AVG(g2.g_price),0)
				 FROM
						tb_goods AS g2);
                        
--ROUND(숫자,0)=뒷 소숫점 자리에서 올림 처리//0번째 자리에서 올림처리
- 서브 쿼리에서 WHERE OR / AND 등 대신 다중 행 연산자(IN,ALL 등)를 사용하면 간략하게 표현 가능하다.
  ex) country code = "kor" or country code = "usa" or country code = "eur" -> country code IN(kor,usa,eur) 
- 주의 점 : 메인 쿼리의 WHERE절에서 비교하는 컬럼 수와 서브 쿼리에서 조회하는 컬럼의 수 일치
SELECT
*
FROM
	CITY AS C
WHERE
	(C.CountryCode, C.Name) = (SELECT
                                *
                                FROM
                                    city AS c1
                                WHERE
                                    c1.CountryCode = 'KOR');
                                    
-- 오류 발생


SELECT
*
FROM
	CITY AS C
WHERE
	(C.CountryCode, C.Name) IN(SELECT
                                c1.CountryCode, c1.Name
                                FROM
                                    city AS c1
                                WHERE
                                    c1.CountryCode = 'KOR');


- IN 연산자

  리턴되는 값 중 조건에 해당하는 값이 있으면 참 (or)
  WHERE 컬럼 = 값 (country code = "kor" / 비교 연산자 = 는 반환되는 값이 단일 행일 경우 사용한다.)
  IN 연산자를 사용할 경우 반환되는 값이 여러 개 있을 경우, 다중 행을 반환할 수 있다.

- ANY, SOME 연산자
  서브 쿼리에 의해 리턴되는 가각의 값과 조건을 비교하여 하나 이상을 만족하면 참 (or)
  ANY와 SOME 차이 없음. 똑같은 기능
  IN과 ANY, SOME의 차이점 : IN은 = 일 경우만 사용 가능 / ANY,SOME은 =,>,< 등 모두 사용 가능
SELECT
*
FROM
	tb_member AS m
WHERE
	m.m_level > ANY (select
						l.level_num
					from
						tb_member_level AS l
					where
						l.level_num>1);
-- l.level_num은 총 1,2,3가 존재
-- 서브 쿼리에서 where절의 조건문에 따라 서브 쿼리의 값은 2,3
-- ANY는 or조건이기 때문에
-- 서브 쿼리에서 2 or 3 결과가 반환돼서
-- 메인 쿼리에서 m.m_level > 2 or m.m_level > 3 와 같은 결과가 반환된다.
-- 때문에 m.m_level = 3인 결과만 반환된다.
-- 메인 쿼리의 WHERE절에서 m.m_level >= ANY 로 작성할 경우 2,3 둘다 반환된다.

- 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);
-- 서브 쿼리의 값이 2 or 3
-- ALL은 and조건이기 때문에
-- 메인 쿼리에서 m.m_level >=2 and m.m_level >=3 결과 반환
-- 때문에 m.m_level이 3만 반환된다.

- EXISTS, NOT EXISTS
  데이터의 존재 유무 확인
  메인 쿼리의 비교 조건이 서브 쿼리의 결과 중에서 만족하는 값이 하나라도 존재하면 참 (or)
  IN과 EXISTS의 차이점 :  EXISTS는 동일한 컬럼에서 서브 쿼리의 값이 메인 쿼리의 값에 존재하는 지 체크하는 용도
SELECT
*
FROM
 	tb_member AS m
WHERE
	EXISTS (select
				*
			from
				tb_order AS o 
			WHERE
				m.m_id =o.o_id);
                
-- tb_member 테이블의 m_id 중에 tb_order 테이블의 o_id와 일치하는 값을 반환

[ **단일 행 반환 / 다중 행 반환에 따른 분류 ]
- 단일 행 서브쿼리
  서브쿼리에서 하나의 행을 반환해 메인쿼리에 전달 한다.
  스칼라 서브쿼리는 단일행 부속 질의,비교 연산자에서 주로 나타남​

- 다중행 서브쿼리
  서브 쿼리 결과가 여러 개의 행을 반환하는 경우.
  IN 연산자를 사용하여 여러 행을 처리 할 수 있다.


[[ 쿼리 실행 순서 ]]
- 서브 쿼리의 값 먼저 반환 -> 메인 쿼리에서 비교
- 인라인 뷰 : 서브 쿼리 먼저 실행 -> 메인 쿼리 실행 (기본 쿼리 실행 순서가 FROM부터 이기 때문)
- 중첩 질의 : 메인쿼리 실행 -> 서브 쿼리 실행

[[ 작성 시, 주의 사항 ]]
- 서브 쿼리는 반드시 소괄호() 안에 작성한다.
- 서브 쿼리 안에 ORDER BY 사용 불가능
- 연산자 오른쪽에 작성한다.

 

728x90