3. SQL - subquery

Jasonยท2026๋…„ 1์›” 12์ผ

SQL

๋ชฉ๋ก ๋ณด๊ธฐ
3/47

WHERE/FROM/SELECT์ ˆ, CTE

SQL ์„œ๋ธŒ์ฟผ๋ฆฌ โ€” ์ฟผ๋ฆฌ ์•ˆ์˜ ์ฟผ๋ฆฌ

๐Ÿ’ก ๋ณต์žกํ•œ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ํ•ด๊ฒฐํ•˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ์™„๋ฒฝ ์ •๋ฆฌํ•œ๋‹ค.


๐Ÿค” ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋ญ์•ผ?

์ฟผ๋ฆฌ ์•ˆ์— ๋“ค์–ด๊ฐ€๋Š” ๋˜ ๋‹ค๋ฅธ ์ฟผ๋ฆฌ.

๋ณต์žกํ•œ ์งˆ๋ฌธ์„ ๋‹จ๊ณ„๋ณ„๋กœ ๋‚˜๋ˆ ์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

-- "ํ‰๊ท ๋ณด๋‹ค ๋งŽ์ด ์ฃผ๋ฌธํ•œ ์œ ์ €๋Š”?"
SELECT * FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ์„œ๋ธŒ์ฟผ๋ฆฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“Š ์˜ˆ์ œ ํ…Œ์ด๋ธ”

[users]
| user_id | name | created_at |
|---------|------|------------|
| 1 | ๊น€์ฒ ์ˆ˜ | 2024-01-01 |
| 2 | ์ด์˜ํฌ | 2024-01-15 |
| 3 | ๋ฐ•๋ฏผ์ˆ˜ | 2024-02-01 |

[orders]
| order_id | user_id | amount | order_date |
|----------|---------|--------|------------|
| 101 | 1 | 150000 | 2024-01-10 |
| 102 | 1 | 50000 | 2024-01-20 |
| 103 | 2 | 200000 | 2024-02-05 |
| 104 | 2 | 80000 | 2024-02-10 |
| 105 | 3 | 300000 | 2024-02-15 |


๐Ÿ—‚๏ธ ์„œ๋ธŒ์ฟผ๋ฆฌ ์œ„์น˜๋ณ„ ๋ถ„๋ฅ˜

์„œ๋ธŒ์ฟผ๋ฆฌ๋Š” ์–ด๋””์— ์œ„์น˜ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

SELECT (์„œ๋ธŒ์ฟผ๋ฆฌ)  โ† ์Šค์นผ๋ผ ์„œ๋ธŒ์ฟผ๋ฆฌ
FROM (์„œ๋ธŒ์ฟผ๋ฆฌ)    โ† ์ธ๋ผ์ธ ๋ทฐ
WHERE (์„œ๋ธŒ์ฟผ๋ฆฌ)   โ† ์ค‘์ฒฉ ์„œ๋ธŒ์ฟผ๋ฆฌ

1๏ธโƒฃ WHERE ์ ˆ ์„œ๋ธŒ์ฟผ๋ฆฌ

๋‹จ์ผ ๊ฐ’ ๋ฐ˜ํ™˜

-- ํ‰๊ท  ์ฃผ๋ฌธ๊ธˆ์•ก๋ณด๋‹ค ํฐ ์ฃผ๋ฌธ ์กฐํšŒ
SELECT *
FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);

์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ’(156,000)์„ ๋ฐ˜ํ™˜ โ†’ ๋น„๊ต ๊ฐ€๋Šฅ

์—ฌ๋Ÿฌ ๊ฐ’ ๋ฐ˜ํ™˜ (IN)

-- ์ฃผ๋ฌธํ•œ ์  ์žˆ๋Š” ์œ ์ € ์กฐํšŒ
SELECT *
FROM users
WHERE user_id IN (SELECT DISTINCT user_id FROM orders);

์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐ’(1, 2, 3)์„ ๋ฐ˜ํ™˜ โ†’ IN์œผ๋กœ ๋น„๊ต

์—ฌ๋Ÿฌ ๊ฐ’ ๋ฐ˜ํ™˜ (NOT IN)

-- ์ฃผ๋ฌธํ•œ ์  ์—†๋Š” ์œ ์ € ์กฐํšŒ
SELECT *
FROM users
WHERE user_id NOT IN (SELECT DISTINCT user_id FROM orders);

2๏ธโƒฃ FROM ์ ˆ ์„œ๋ธŒ์ฟผ๋ฆฌ (์ธ๋ผ์ธ ๋ทฐ)

์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ž„์‹œ ํ…Œ์ด๋ธ”์ฒ˜๋Ÿผ ์‚ฌ์šฉ.

-- ์œ ์ €๋ณ„ ์ด ์ฃผ๋ฌธ๊ธˆ์•ก ๊ตฌํ•œ ๋’ค, 100,000์› ์ด์ƒ๋งŒ ํ•„ํ„ฐ
SELECT *
FROM (
    SELECT 
        user_id,
        SUM(amount) AS total_amount
    FROM orders
    GROUP BY user_id
) AS user_totals  -- ๋ณ„์นญ ํ•„์ˆ˜!
WHERE total_amount >= 100000;
user_idtotal_amount
1200000
2280000
3300000

ํฌ์ธํŠธ: FROM ์ ˆ ์„œ๋ธŒ์ฟผ๋ฆฌ๋Š” ๋ณ„์นญ(AS) ํ•„์ˆ˜!


3๏ธโƒฃ SELECT ์ ˆ ์„œ๋ธŒ์ฟผ๋ฆฌ (์Šค์นผ๋ผ ์„œ๋ธŒ์ฟผ๋ฆฌ)

๊ฐ ํ–‰๋งˆ๋‹ค ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์–ด ํ•˜๋‚˜์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜.

-- ๊ฐ ์ฃผ๋ฌธ์— ์ „์ฒด ํ‰๊ท  ๊ธˆ์•ก ํ•จ๊ป˜ ํ‘œ์‹œ
SELECT 
    order_id,
    amount,
    (SELECT AVG(amount) FROM orders) AS avg_amount
FROM orders;
order_idamountavg_amount
101150000156000
10250000156000
103200000156000
.........

์—ฐ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ (์Šค์นผ๋ผ)

-- ๊ฐ ์œ ์ €์˜ ์ฃผ๋ฌธ ๊ฑด์ˆ˜๋ฅผ ํ•จ๊ป˜ ํ‘œ์‹œ
SELECT 
    u.user_id,
    u.name,
    (SELECT COUNT(*) 
     FROM orders o 
     WHERE o.user_id = u.user_id) AS order_count
FROM users u;
user_idnameorder_count
1๊น€์ฒ ์ˆ˜2
2์ด์˜ํฌ2
3๋ฐ•๋ฏผ์ˆ˜1

4๏ธโƒฃ ์—ฐ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ vs ๋น„์—ฐ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ

๋น„์—ฐ๊ด€ (Non-correlated)

์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋จ. ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰.

-- ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋ฉ”์ธ์ฟผ๋ฆฌ์™€ ๋ฌด๊ด€
SELECT * FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);

์—ฐ๊ด€ (Correlated)

์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋ฉ”์ธ์ฟผ๋ฆฌ์˜ ๊ฐ’์„ ์ฐธ์กฐ. ํ–‰๋งˆ๋‹ค ์‹คํ–‰.

-- ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋ฉ”์ธ์ฟผ๋ฆฌ์˜ user_id๋ฅผ ์ฐธ์กฐ
SELECT * FROM orders o1
WHERE amount > (
    SELECT AVG(amount) 
    FROM orders o2 
    WHERE o2.user_id = o1.user_id  -- ๋ฉ”์ธ์ฟผ๋ฆฌ ์ฐธ์กฐ!
);

โ†’ "์ž๊ธฐ ์œ ์ €์˜ ํ‰๊ท ๋ณด๋‹ค ํฐ ์ฃผ๋ฌธ"


5๏ธโƒฃ EXISTS / NOT EXISTS

EXISTS

์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ (True/False)

-- ์ฃผ๋ฌธ์ด ์žˆ๋Š” ์œ ์ €๋งŒ ์กฐํšŒ
SELECT *
FROM users u
WHERE EXISTS (
    SELECT 1 
    FROM orders o 
    WHERE o.user_id = u.user_id
);

NOT EXISTS

-- ์ฃผ๋ฌธ์ด ์—†๋Š” ์œ ์ € ์กฐํšŒ
SELECT *
FROM users u
WHERE NOT EXISTS (
    SELECT 1 
    FROM orders o 
    WHERE o.user_id = u.user_id
);

IN vs EXISTS

์ƒํ™ฉ์ถ”์ฒœ
์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์ž‘์„ ๋•ŒIN
์„œ๋ธŒ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ํด ๋•ŒEXISTS
NULL ์ฒ˜๋ฆฌ ํ•„์š”ํ•  ๋•ŒEXISTS

6๏ธโƒฃ ์‹ค์ „ ํŒจํ„ด

ํŒจํ„ด 1: ์ตœ๋Œ€/์ตœ์†Œ๊ฐ’ ๊ฐ€์ง„ ํ–‰ ์ฐพ๊ธฐ

-- ๊ฐ€์žฅ ํฐ ์ฃผ๋ฌธ ์ฐพ๊ธฐ
SELECT *
FROM orders
WHERE amount = (SELECT MAX(amount) FROM orders);

ํŒจํ„ด 2: ๊ทธ๋ฃน๋ณ„ ์ตœ๋Œ€๊ฐ’ ํ–‰ ์ฐพ๊ธฐ

-- ์œ ์ €๋ณ„ ๊ฐ€์žฅ ํฐ ์ฃผ๋ฌธ ์ฐพ๊ธฐ
SELECT *
FROM orders o1
WHERE amount = (
    SELECT MAX(amount)
    FROM orders o2
    WHERE o2.user_id = o1.user_id
);

ํŒจํ„ด 3: Top N ๊ตฌํ•˜๊ธฐ

-- ์ฃผ๋ฌธ ๊ธˆ์•ก ์ƒ์œ„ 3๊ฐœ
SELECT *
FROM orders
WHERE amount >= (
    SELECT MIN(amount)
    FROM (
        SELECT amount
        FROM orders
        ORDER BY amount DESC
        LIMIT 3
    ) AS top3
);

ํŒจํ„ด 4: ์ด์ „ ๊ฐ’๊ณผ ๋น„๊ต

-- ์ง์ „ ์ฃผ๋ฌธ๋ณด๋‹ค ๊ธˆ์•ก์ด ํฐ ์ฃผ๋ฌธ ์ฐพ๊ธฐ
SELECT *
FROM orders o1
WHERE amount > (
    SELECT amount
    FROM orders o2
    WHERE o2.order_id = o1.order_id - 1
);

โ†’ ์œˆ๋„์šฐ ํ•จ์ˆ˜(LAG)๋กœ ๋” ์‰ฝ๊ฒŒ ๊ฐ€๋Šฅ!


7๏ธโƒฃ WITH (CTE) โ€” ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ์ง„ํ™”

CTE (Common Table Expression): ์„œ๋ธŒ์ฟผ๋ฆฌ์— ์ด๋ฆ„์„ ๋ถ™์—ฌ ์žฌ์‚ฌ์šฉ

-- ์„œ๋ธŒ์ฟผ๋ฆฌ ๋ฐฉ์‹
SELECT *
FROM (
    SELECT user_id, SUM(amount) AS total
    FROM orders
    GROUP BY user_id
) AS user_totals
WHERE total > 100000;

-- CTE ๋ฐฉ์‹ (๋” ๊น”๋”!)
WITH user_totals AS (
    SELECT user_id, SUM(amount) AS total
    FROM orders
    GROUP BY user_id
)
SELECT *
FROM user_totals
WHERE total > 100000;

CTE ์žฅ์ 

  1. ๊ฐ€๋…์„ฑ: ์ฟผ๋ฆฌ๊ฐ€ ์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝํž˜
  2. ์žฌ์‚ฌ์šฉ: ๊ฐ™์€ ์„œ๋ธŒ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  3. ๋””๋ฒ„๊น…: ๋‹จ๊ณ„๋ณ„๋กœ ํ™•์ธ ์‰ฌ์›€

์—ฌ๋Ÿฌ CTE ์‚ฌ์šฉ

WITH 
user_orders AS (
    SELECT user_id, COUNT(*) AS cnt, SUM(amount) AS total
    FROM orders
    GROUP BY user_id
),
vip_users AS (
    SELECT user_id
    FROM user_orders
    WHERE total >= 200000
)
SELECT u.name, uo.cnt, uo.total
FROM users u
JOIN user_orders uo ON u.user_id = uo.user_id
WHERE u.user_id IN (SELECT user_id FROM vip_users);

๐ŸŽฏ ์‹ค์ „ ๋ฌธ์ œ

๋ฌธ์ œ 1: WHERE ์„œ๋ธŒ์ฟผ๋ฆฌ

์ „์ฒด ํ‰๊ท  ์ฃผ๋ฌธ๊ธˆ์•ก๋ณด๋‹ค ํฐ ์ฃผ๋ฌธ์˜ order_id, amount๋ฅผ ๊ตฌํ•˜์„ธ์š”.

์ •๋‹ต ๋ณด๊ธฐ
SELECT order_id, amount
FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);

๋ฌธ์ œ 2: IN ์„œ๋ธŒ์ฟผ๋ฆฌ

2024๋…„ 2์›”์— ์ฃผ๋ฌธํ•œ ์œ ์ €์˜ ์ด๋ฆ„์„ ๊ตฌํ•˜์„ธ์š”.

์ •๋‹ต ๋ณด๊ธฐ
SELECT name
FROM users
WHERE user_id IN (
    SELECT DISTINCT user_id
    FROM orders
    WHERE order_date >= '2024-02-01'
      AND order_date < '2024-03-01'
);

๋ฌธ์ œ 3: NOT EXISTS

์ฃผ๋ฌธ์„ ํ•œ ๋ฒˆ๋„ ํ•˜์ง€ ์•Š์€ ์œ ์ €๋ฅผ ๊ตฌํ•˜์„ธ์š”.

์ •๋‹ต ๋ณด๊ธฐ
SELECT *
FROM users u
WHERE NOT EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.user_id = u.user_id
);

๋˜๋Š”:

SELECT *
FROM users
WHERE user_id NOT IN (SELECT DISTINCT user_id FROM orders);

๋ฌธ์ œ 4: ์—ฐ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ

๊ฐ ์œ ์ €์˜ ํ‰๊ท  ์ฃผ๋ฌธ๊ธˆ์•ก๋ณด๋‹ค ํฐ ์ฃผ๋ฌธ์„ ์ฐพ์œผ์„ธ์š”.

์ •๋‹ต ๋ณด๊ธฐ
SELECT *
FROM orders o1
WHERE amount > (
    SELECT AVG(amount)
    FROM orders o2
    WHERE o2.user_id = o1.user_id
);

๋ฌธ์ œ 5: CTE ํ™œ์šฉ

์ด ์ฃผ๋ฌธ๊ธˆ์•ก ๊ธฐ์ค€ ์ƒ์œ„ 2๋ช…์˜ ์œ ์ € ์ด๋ฆ„๊ณผ ์ด์•ก์„ ๊ตฌํ•˜์„ธ์š”.

์ •๋‹ต ๋ณด๊ธฐ
WITH user_totals AS (
    SELECT 
        user_id,
        SUM(amount) AS total_amount
    FROM orders
    GROUP BY user_id
    ORDER BY total_amount DESC
    LIMIT 2
)
SELECT u.name, ut.total_amount
FROM user_totals ut
JOIN users u ON ut.user_id = u.user_id;

๐Ÿ’ก ์„œ๋ธŒ์ฟผ๋ฆฌ vs JOIN vs CTE

์ƒํ™ฉ์ถ”์ฒœ
๋‹จ์ˆœ ํ•„ํ„ฐ๋ง์„œ๋ธŒ์ฟผ๋ฆฌ
์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์กฐํ•ฉJOIN
๋ณต์žกํ•œ ๋‹จ๊ณ„๋ณ„ ์ฒ˜๋ฆฌCTE
๊ฐ™์€ ๊ฒฐ๊ณผ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉCTE

์„ฑ๋Šฅ ํŒ:

  • ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด โ†’ JOIN์ด๋‚˜ CTE๋กœ ๋ณ€ํ™˜ ๊ณ ๋ ค
  • ์—ฐ๊ด€ ์„œ๋ธŒ์ฟผ๋ฆฌ๋Š” ํ–‰๋งˆ๋‹ค ์‹คํ–‰ โ†’ ๋Œ€์šฉ๋Ÿ‰์—์„œ ๋А๋ฆด ์ˆ˜ ์žˆ์Œ

๐Ÿ“ ํ•ต์‹ฌ ์ •๋ฆฌ

[์„œ๋ธŒ์ฟผ๋ฆฌ ์œ„์น˜]
WHERE์ ˆ: ํ•„ํ„ฐ๋ง ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉ
FROM์ ˆ:  ์ž„์‹œ ํ…Œ์ด๋ธ”์ฒ˜๋Ÿผ ์‚ฌ์šฉ (์ธ๋ผ์ธ ๋ทฐ)
SELECT์ ˆ: ๊ฐ ํ–‰์— ๊ฐ’ ์ถ”๊ฐ€ (์Šค์นผ๋ผ)

[์ฃผ์š” ์—ฐ์‚ฐ์ž]
IN / NOT IN: ์—ฌ๋Ÿฌ ๊ฐ’๊ณผ ๋น„๊ต
EXISTS / NOT EXISTS: ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
๋น„๊ต์—ฐ์‚ฐ์ž: ๋‹จ์ผ ๊ฐ’๊ณผ ๋น„๊ต

[CTE]
WITH ์ ˆ๋กœ ์„œ๋ธŒ์ฟผ๋ฆฌ์— ์ด๋ฆ„ ๋ถ™์—ฌ ์žฌ์‚ฌ์šฉ
๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์— ์ข‹์Œ

๐Ÿ”— ๋‹ค์Œ ํฌ์ŠคํŠธ

[SQL ์‹œ๋ฆฌ์ฆˆ #4] ์œˆ๋„์šฐ ํ•จ์ˆ˜ โ€” ROW_NUMBER, RANK, LAG์˜ ๋งˆ๋ฒ•


๐Ÿ“ ์„œ๋ธŒ์ฟผ๋ฆฌ๋Š” ๋ณต์žกํ•œ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋„๊ตฌ๋‹ค.
CTE๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋” ๊น”๋”ํ•˜๊ณ  ์ฝ๊ธฐ ์ข‹์€ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค!

profile
Data Analyst | Thoughts Become Things. ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํ•  ์ˆ˜ ์—†๋‹ค. | www.linkedin.com/in/๋ช…์ˆ˜-์ œ-7ab843200

0๊ฐœ์˜ ๋Œ“๊ธ€