์ŠคํŒŒ๋ฅดํƒ€ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„(25.12.01~)

[๊ฐœ๋…์ •๋ฆฌ] ์„œ๋ธŒ์ฟผ๋ฆฌ(Subquery)

0๏ธโƒฃ 2025. 12. 31. 00:41

๋‹ค๋ฅธ SQL ์•ˆ์— ๋“ค์–ด์žˆ๋Š” SELECT

MySQL์€ ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ˜•ํƒœ๊ฐ€ 4์ข…๋ฅ˜

  • scalar(๊ฐ’ 1๊ฐœ)
  • row(ํ–‰ 1๊ฐœ)
  • column(์—ด 1๊ฐœ = ๋ฆฌ์ŠคํŠธ)
  • table(ํ‘œ)

<Scalar Subquery; ๋…๋ฆฝ๋œ ๊ฐ’1๊ฐœ๋กœ ๋ฐ˜ํ™˜>

Q. ํ‰๊ท ๋ณด๋‹ค ๋น„์‹ผ ๊ฐ•์ขŒ ์ฐพ๊ธฐ
(1. ์„œ๋ธŒ ์ฟผ๋ฆฌ)๋ชจ๋“  ๊ฐ•์ขŒ ํ‰๊ท  ๊ตฌํ•˜๊ธฐ (2. ๋ฐ”๊นฅ ์ฟผ๋ฆฌ) ๊ทธ๊ฒƒ๋ณด๋‹ค ๋น„์‹ผ ๊ฐ•์ขŒ ์ฐพ๊ธฐ
SELECT
  course_id,
  course_name,
  list_price
FROM basic.courses
WHERE list_price > (SELECT AVG(list_price) FROM basic.courses)
ORDER BY list_price DESC;
  • (SELECT AVG(list_price) FROM basic.courses) = 120428.5714
    - ๋ฐ”๊นฅ ์ฟผ๋ฆฌ์™€ ์—ฐ๋™๋˜์ง€ ์•Š์€ ๋…๋ฆฝ๋œ ๊ฐ’ ์ถœ๋ ฅ
  • ์„œ๋ธŒ ์ฟผ๋ฆฌ ๋‹จ๋… ์‹คํ–‰ ์‹œ, ์ž‘๋™ํ•จ.
  • where์—๋Š” ์ง‘๊ณ„ํ•จ์ˆ˜ ์‚ฌ์šฉ ๋ถˆ๊ฐ€, ์„œ๋ธŒ์ฟผ๋ฆฌ ํ˜•ํƒœ ์ด์šฉ.

<Column Subquery; ์—ด1๊ฐœ, ๋ฆฌ์ŠคํŠธ>
IN()

Q. 'sql ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ•์ขŒ'๋ฅผ ์‹ ์ฒญํ•œ ๋‚ด์—ญ๋งŒ ๋ณด๊ธฐ
(1. ์„œ๋ธŒ ์ฟผ๋ฆฌ) T.courses - C.category = 'sql'
(2. ๋ฐ”๊นฅ ์ฟผ๋ฆฌ) T.enrollments
  • (T)courses์™€ (T)enrollments์˜ ๊ณตํ†ต ์ปฌ๋Ÿผ: course_id
    (*๊ฒฐ๊ตญ ๋‘ ๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ์—ฐ๋™๋˜๋Š” ๊ฒƒ - โญ๏ธ ๊ณตํ†ต ์ปฌ๋Ÿผ์„ ์—ฐ๋™ํ•ด์ฃผ๋Š” ๊ฒŒ ์ค‘์š”)
  • IN ( ... ) ์•ˆ์ชฝ์€ ํ•œ ์ปฌ๋Ÿผ์งœ๋ฆฌ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋‚˜์˜ค๋ฉด ์„ฑ๊ณต
SELECT enrollment_id, final_price
FROM basic.enrollments
where course_id IN 
	(
	SELECT course_id -- ํ•œ ์ค„์งœ๋ฆฌ ์ปฌ๋Ÿผ ์ถœ๋ ฅ
	FROM basic.courses
	where category = 'sql'
	);
  • ์„œ๋ธŒ์ฟผ๋ฆฌ ์ปฌ๋Ÿผ์„ ์—ฌ๋Ÿฌ ๊ฐœ ์ถœ๋ ฅํ•˜๋ฉด, ์—๋Ÿฌ.


<Table Subquery; ํ‘œ>
EXISTS / NOT EXISTS (“ํ•œ ๋ฒˆ์ด๋ผ๋„ ์žˆ๋‹ค / ํ•œ ๋ฒˆ๋„ ์—†๋‹ค” ์ฐพ๊ธฐ์— ์šฉ์ด)

  • ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ SELECT๋Š” ๊ฒฐ๊ณผ์— ๋ฌด๊ด€ (select *, select 1, select 5 ๋“ฑ ์–ด๋–ค ๊ฑธ ์จ๋„ ๊ฒฐ๊ณผ ๋™์ผ)
  • ๋‘ ํ…Œ์ด๋ธ”์˜ ๊ณตํ†ต ์ปฌ๋Ÿผ์„ where์—์„œ ๋ฌถ์–ด์คŒ. (= ๋‘ ํ…Œ์ด๋ธ”์— ๋ชจ๋‘ ์กด์žฌํ•˜๋Š” ํ–‰ ์ฐพ๊ธฐ)
  • exists: ๋งค์นญ๋˜๋Š” ๊ฒŒ ์žˆ๋Š” ๊ฒƒ / not exists: ๋งค์นญ๋˜์ง€ ์•Š๋Š” ๊ฒƒ.
  • True/False ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฑฐ์—ฌ์„œ, ์„œ๋ธŒ ์ฟผ๋ฆฌ ๋‚ด ๊ฒฐ๊ณผ ํ–‰์„ ๋ฐ–์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Œ.
Q. ์ˆ˜๊ฐ•์‹ ์ฒญ์„ "ํ•œ ๋ฒˆ์ด๋ผ๋„ ํ•œ / ํ•œ ๋ฒˆ๋„ ์•ˆ ํ•œ" ํ•™์ƒ ์ฐพ๊ธฐ
(1. ์„œ๋ธŒ ์ฟผ๋ฆฌ) (T)enrollments โšฏ (T)students
(2. ๋ฐ”๊นฅ ์ฟผ๋ฆฌ) (T)students ์ถœ๋ ฅ
-- ํ•œ ๋ฒˆ์ด๋ผ๋„ ํ•œ ํ•™์ƒ ์ฐพ๊ธฐ
SELECT s.student_id,
	   s.student_name
FROM basic.students s
where exists
	(
	select *
	from basic.enrollments e
	where e.student_id=s.student_id
	);

-- ํ•œ ๋ฒˆ๋„ ์•ˆ ํ•œ ํ•™์ƒ ์ฐพ๊ธฐ
SELECT s.student_id,
	   s.student_name
FROM basic.students s
where not exists
	(
	select *
	from basic.enrollments e
	where e.student_id=s.student_id
	);

<FROM ์„œ๋ธŒ์ฟผ๋ฆฌ(ํŒŒ์ƒ ํ…Œ์ด๋ธ”)>
From์—์„œ ์‚ฌ์šฉํ•  ํ…Œ์ด๋ธ”์„ ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ ์ƒ์„ฑ

  • JOIN์—์„œ N์ชฝ ํ…Œ์ด๋ธ”์„ '์šฐ์„  ์š”์•ฝ'ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…
  • FROM ํ…Œ์ด๋ธ” ์ด๋ฏธ ํ•„ํ„ฐ๋ง๋œ ์ƒํƒœ → ์ตœ์ข… ๊ฒฐ๊ณผ์—์„œ ํ–‰์ด ๋Š˜์–ด๋‚˜์ง€ ์•Š์Œ.
  • AS ๋ณ„์นญ ํ•„์ˆ˜
Q. ๊ฐ•์ขŒ๋ณ„ ์‹ ์ฒญ ๊ฑด์ˆ˜ "์š”์•ฝํ‘œ" ๋งŒ๋“ค๊ธฐ
(1. ์„œ๋ธŒ ์ฟผ๋ฆฌ) ์‹ ์ฒญ ๊ฑด์ˆ˜ ํ…Œ์ด๋ธ” ๋งŒ๋“ค๊ธฐ
(2. ๋ฐ”๊นฅ ์ฟผ๋ฆฌ) ๊ฐ•์ขŒ๋ณ„๋กœ ์ถœ๋ ฅํ•˜๊ธฐ
-- ๊ฐ•์ขŒ๋ณ„
SELECT c.course_id,
       c.course_name,
       ifnull(enr.enroll_cnt,0)
from basic.courses c
left join (-- ์‹ ์ฒญ๊ฑด์ˆ˜
	select course_id,
		   count(*) as enroll_cnt
	from basic.enrollments
	group by course_id
	) as enr
ON c.course_id=enr.course_id;
  • ์„œ๋ธŒ์ฟผ๋ฆฌ ์•ˆ์—์„œ course_id๋กœ ์ด๋ฏธ group byํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—,
    ๋ฐ”๊นฅ ์ฟผ๋ฆฌ์—์„œ ์ถ”๊ฐ€๋กœ ๊ทธ๋ฃนํ•‘ํ•  ํ•„์š” X

<CTE (WITH) : ์ด๋ฆ„ ๋ถ™์ธ ํŒŒ์ƒํ…Œ์ด๋ธ”>

  • ์ด๋ฆ„์ด ์žˆ๋Š” ์ž„์‹œ ๊ฒฐ๊ณผ
WITH ์ด๋ฆ„ AS (
  SELECT ...
)
SELECT *
FROM ์ด๋ฆ„;

 

Q. "active ์‹ ์ฒญ๋งŒ" ๋จผ์ € ๊ณจ๋ผ์„œ ๋ณด๊ธฐ
(1. CTE ์„œ๋ธŒ ์ฟผ๋ฆฌ) active ๊ฒฐ๊ณผ๋งŒ ๋ฝ‘์•„์„œ ์ž„์‹œํ‘œ ๋งŒ๋“ค๊ธฐ
(2. ๋ฐ”๊นฅ ์ฟผ๋ฆฌ) ์ž„์‹œํ‘œ์—์„œ ์ถœ๋ ฅํ•˜๊ธฐ
WITH active_enrollment as
	(-- CTE
        select enrollment_id,
               student_id,
               course_id,
               enroll_date
        from basic.enrollments
        where enrollment_status = 'active'
        )
-- ๋ฐ”๊นฅ ์ฟผ๋ฆฌ
select *
from active_enrollment;

<CTE (WITH) + CASE ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ธฐ>

  • CTE ํŒŒ์ƒํ…Œ์ด๋ธ” ์ƒ์„ฑ
  • 'CTE' JOIN '์›๋ž˜ ํ…Œ์ด๋ธ”(case when ์‚ฌ์šฉ)' => ๊ฒฐ๊ณผ
Q. ํ•™์ƒ๋ณ„ ์‹ ์ฒญ๊ฑด์ˆ˜ ๊ตฌํ•˜๊ธฐ + ๊ฑด์ˆ˜์— ๋”ฐ๋ฅธ ๋ผ๋ฒจ๋ง
(1. CTE) ํŒŒ์ƒํ…Œ์ด๋ธ”: ํ•™์ƒ๋ณ„ ์‹ ์ฒญ๊ฑด์ˆ˜
(2. JOIN) ํ•™์ƒ๋ณ„ join ์‹ ์ฒญ๊ฑด์ˆ˜(→CTE)
                + ๋ผ๋ฒจ๋ง case when ์กฐ๊ฑด
WITH enr_by_stu AS (
	SELECT student_id,
	       count(*) AS enroll_cnt
	FROM basic.enrollments
	GROUP BY student_id
	)
SELECT s.student_id,
       s.student_name,
       ifnull(enroll_cnt, 0) AS enroll_cnt,
       CASE WHEN ifnull(e.enroll_cnt, 0) = 0 THEN 'no_enroll'
         	WHEN e.enroll_cnt >= 1 THEN 'has_enroll'
        	END AS enroll_flag
FROM basic.students s
	LEFT JOIN enr_by_stu e
	ON s.student_id=e.student_id;

<์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ(Correlated Subquery)>

: ์„œ๋ธŒ์ฟผ๋ฆฌ ์•ˆ์—์„œ ๋ฐ”๊นฅ ์ฟผ๋ฆฌ์˜ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ์„ ์ฐธ์กฐํ•˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ

“๋ฐ”๊นฅ ์ฟผ๋ฆฌ์—์„œ ํ•œ ํ–‰์„ ๊บผ๋‚ผ ๋•Œ๋งˆ๋‹ค, ๊ทธ ํ–‰์— ๋งž์ถฐ์„œ ์•ˆ์ชฝ ์ฟผ๋ฆฌ๊ฐ€ ๊ณ„์‚ฐ๋œ๋‹ค”

  • ๋น„์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ: ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
  • ์ƒ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ: ๋ฐ”๊นฅ ์ฟผ๋ฆฌ์˜ ํ•œ ํ–‰์„ ์ถœ๋ ฅํ•  ๋•Œ๋งˆ๋‹ค, ๊ทธ ํ–‰์— ๋งค์นญ๋˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.
SELECT
  s.student_id,
  s.student_name,
  (
    SELECT COUNT(*)
    FROM basic.enrollments e
    WHERE e.student_id = s.student_id
  ) AS enroll_cnt
FROM basic.students s
ORDER BY s.student_id;