๐ ์๋ธ์ฟผ๋ฆฌ - ์ค์ฒฉ๋ SQL๋ฌธ ์ฌ์ฉํ๊ธฐ
์ด๋ฒ ๋จ๊ณ์์๋ ์๋ธ์ฟผ๋ฆฌ(Subquery, ์ค์ฒฉ ์ฟผ๋ฆฌ)์ ๋ํด ๋ฐฐ์๋ณด๊ฒ ์ต๋๋ค.
์๋ธ์ฟผ๋ฆฌ๋ SQL๋ฌธ ๋ด๋ถ์ ํฌํจ๋ ๋ ๋ค๋ฅธ SQL๋ฌธ์ผ๋ก, ๋ณต์กํ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ์ ์ฉํฉ๋๋ค.
6-1. ์๋ธ์ฟผ๋ฆฌ์ ๊ฐ๋
์๋ธ์ฟผ๋ฆฌ๋ ๋ฉ์ธ ์ฟผ๋ฆฌ(Main Query) ์์์ ์คํ๋๋ ์์ ์ฟผ๋ฆฌ์ ๋๋ค.
- SELECT, FROM, WHERE, HAVING ๋ฑ ๋ค์ํ ๊ณณ์์ ์ฌ์ฉ ๊ฐ๋ฅ
- ์๋ธ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ ๋จ์ผ ๊ฐ(์ค์นผ๋ผ ๊ฐ), ์ฌ๋ฌ ํ(๋ฆฌ์คํธ), ์ฌ๋ฌ ์ด(ํ ์ด๋ธ ํํ)๋ก ๋ฐํ ๊ฐ๋ฅ
โ ์๋ธ์ฟผ๋ฆฌ ๊ธฐ๋ณธ ๋ฌธ๋ฒ
SELECT column_name
FROM table_name
WHERE column_name = (์๋ธ์ฟผ๋ฆฌ);
- ์๋ธ์ฟผ๋ฆฌ๋
()
๊ดํธ ์์ ์์ฑํด์ผ ํจ
๐น ์๋ธ์ฟผ๋ฆฌ์ ์ ํ
์๋ธ์ฟผ๋ฆฌ ์ ํ | ์ค๋ช | ํน์ง |
---|---|---|
์ค์นผ๋ผ ์๋ธ์ฟผ๋ฆฌ | ๋จ์ผ ๊ฐ(1ํ 1์ด) ๋ฐํ | WHERE, SELECT, HAVING ์ ์์ ์์ฃผ ์ฌ์ฉ |
๋ค์ค ํ ์๋ธ์ฟผ๋ฆฌ | ์ฌ๋ฌ ํ ๋ฐํ (1์ปฌ๋ผ ์ฌ๋ฌ ํ) | IN, ANY, ALL, EXISTS์ ํจ๊ป ์ฌ์ฉ |
๋ค์ค ์ปฌ๋ผ ์๋ธ์ฟผ๋ฆฌ | ์ฌ๋ฌ ์ปฌ๋ผ ๋ฐํ (2๊ฐ ์ด์ ์ปฌ๋ผ) | IN, EXISTS์ ํจ๊ป (๋ค์ค ์กฐ๊ฑด ๋น๊ต) |
์๊ด ์๋ธ์ฟผ๋ฆฌ | ๋ฉ์ธ ์ฟผ๋ฆฌ์ ๊ฐ ํ๋ง๋ค ์คํ | ๋ฉ์ธ ์ฟผ๋ฆฌ ์ปฌ๋ผ์ ์๋ธ์ฟผ๋ฆฌ์์ ์ฐธ์กฐ โ ๋ฐ๋ณต ์คํ |
๐ท 2. WHERE
์ ์๋ธ์ฟผ๋ฆฌ
6-2. WHERE ์ ์์ ์ฌ์ฉ๋๋ ์๋ธ์ฟผ๋ฆฌ
์๋ธ์ฟผ๋ฆฌ๋ WHERE ์ ์์ ํน์ ๊ฐ์ ๋น๊ตํ ๋ ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด, ๊ธ์ฌ๊ฐ ํ๊ท ๋ณด๋ค ๋์ ์ง์์ ์กฐํํ๋ ๊ฒฝ์ฐ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
โ ํ๊ท ๊ธ์ฌ๋ณด๋ค ๋์ ์ง์ ์กฐํ
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
์ค๋ช
- ์๋ธ์ฟผ๋ฆฌ:
SELECT AVG(salary) FROM employees
โ ์ ์ฒด ํ๊ท ๊ธ์ฌ๋ฅผ ๊ณ์ฐ - ๋ฉ์ธ ์ฟผ๋ฆฌ: ํ๊ท ๊ธ์ฌ๋ณด๋ค ๋์ ์ง์๋ง ์กฐํ
โ ํน์ ๋ถ์์ ์ง์ ์กฐํ
SELECT name, department_id
FROM employees
WHERE department_id = (SELECT id FROM departments WHERE name = 'IT');
IT
๋ถ์์id
๋ฅผ ์ฐพ์์employees
ํ ์ด๋ธ์์ ํด๋น ๋ถ์์ ์ง์๋ง ์กฐํ
โ ์๋ธ์ฟผ๋ฆฌ + IN ์ฐ์ฐ์
-- ํน์ ์์น์ ์๋ ๋ถ์๋ค์ ์ง์ ๋ชฉ๋ก ์กฐํ (IN ์ฌ์ฉ)
SELECT name FROM employees
WHERE department_id IN (SELECT id FROM departments WHERE location = 'Seoul');
IN
์ฐ์ฐ์๋ ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ๋ค์ค ํ์ผ ๋ ์ ์ฉํจIN
์ ๋จ์ ๋น๊ต์ด๋ฏ๋ก ๋๋ ๋ฐ์ดํฐ์์๋ ์ฑ๋ฅ ์ ํ ๋ฐ์ ๊ฐ๋ฅ โEXISTS
์ฌ์ฉ์ด ๋ ์ ์ ํ ์ ์์- ์ถ๊ฐ ์์ :
-- EXISTS ์ฌ์ฉ ์์ (๋๋ ๋ฐ์ดํฐ์์ ๋ ํจ์จ์ )
SELECT name FROM employees e
WHERE EXISTS (SELECT 1 FROM departments d WHERE d.id = e.department_id AND d.location = 'Seoul');
๐ ์ค๋ช :
IN
์ฐ์ฐ์๋ ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ๋ง์ ๊ฒฝ์ฐ ์ฑ๋ฅ ์ ํ ๋ฐ์EXISTS
๋ ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ํ์ด ์๋์ง๋ง ํ์ธ โ ์ต์ ํ ๊ฐ๋ฅ
6-3. FROM ์ ์์ ์ฌ์ฉ๋๋ ์๋ธ์ฟผ๋ฆฌ
์๋ธ์ฟผ๋ฆฌ๋ฅผ FROM
์ ์ ์ฌ์ฉํ๋ฉด ์์ ํ
์ด๋ธ(๊ฐ์ ํ
์ด๋ธ)์ ๋ง๋ค์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
โ ๋ถ์๋ณ ํ๊ท ๊ธ์ฌ ์กฐํ
SELECT department, avg_salary
FROM (SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department) AS dept_avg;
์ค๋ช
- ์๋ธ์ฟผ๋ฆฌ:
employees
ํ ์ด๋ธ์์department
๋ณ ํ๊ท ๊ธ์ฌ๋ฅผ ๊ณ์ฐ - ๋ฉ์ธ ์ฟผ๋ฆฌ: ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ(
dept_avg
)์์department
์ ํ๊ท ๊ธ์ฌ ์กฐํ
โ ๊ธ์ฌ๊ฐ ๋์ ์ง์ ์์ 5๋ช ์กฐํ (ORDER BY + LIMIT)
SELECT * FROM
(SELECT name, salary FROM employees ORDER BY salary DESC LIMIT 5) AS top_salaries;
ORDER BY salary DESC LIMIT 5
๋ก ์์ 5๋ช ์กฐํFROM
์ ์์ ์๋ธ์ฟผ๋ฆฌ๋ Derived Table(ํ์ ํ ์ด๋ธ)์ ๋ง๋ค์ด ํ์ฉํ๋ ๊ฐ๋ โ ์ค์ ํ ์ด๋ธ์ด ์๋ ์คํ ์ ์์ฑ๋ ์ผ์์ ํ ์ด๋ธ- ์ถ๊ฐ ์์ :
-- ๋ถ์๋ณ ํ๊ท ๊ธ์ฌ ์กฐํ (Derived Table ํ์ฉ)
SELECT department, avg_salary
FROM (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) AS dept_avg;
๐ ์ค๋ช :
- ์๋ธ์ฟผ๋ฆฌ๊ฐ ํ์ ํ
์ด๋ธ(
dept_avg
)์ ์์ฑํ์ฌ, ์ดํ SELECT ๋ฌธ์์ ์ฌ์ฉ - ์ฃผ์: ์ผ๋ถ DBMS(MariaDB ํฌํจ)์์๋ ํ์ ํ
์ด๋ธ์ ๋ณ์นญ์ด ํ์ํจ
AS dept_avg
์์ผ๋ฉด ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ
6-4. SELECT ์ ์์ ์ฌ์ฉ๋๋ ์๋ธ์ฟผ๋ฆฌ
์๋ธ์ฟผ๋ฆฌ๋ฅผ SELECT
์ ์์ ์ฌ์ฉํ๋ฉด ์ปฌ๋ผ ๊ฐ์ ๋์ ์ผ๋ก ๊ณ์ฐํ ์ ์์ต๋๋ค.
โ ๊ฐ ์ง์์ ๋ถ์๋ช ์ ํจ๊ป ์กฐํ
SELECT name,
(SELECT name FROM departments WHERE departments.id = employees.department_id) AS department_name
FROM employees;
department_id
๋ฅผ ์ด์ฉํด ๊ฐ ์ง์์ด ์ํ ๋ถ์๋ช ์ ๊ฐ์ ธ์ดSELECT
์ ์๋ธ์ฟผ๋ฆฌ๋ ๊ฐ ํ๋ง๋ค ์คํ๋๋ฏ๋ก ๋นํจ์จ์ - JOIN์ ์ฌ์ฉํ ์ฑ๋ฅ ํฅ์ ๊ฐ๋ฅ
- ๋น๊ต ์์ :
--(๋ ๋์ ๋ฐฉ๋ฒ) JOIN ํ์ฉ
SELECT e.name, d.name AS department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
๐ ์ค๋ช :
SELECT
์ ์์ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ ํ๋ง๋ค ์๋ธ์ฟผ๋ฆฌ๋ฅผ ์คํ โ ์ฑ๋ฅ ์ ํ- JOIN์ ์ฌ์ฉํ๋ฉด ํ ๋ฒ์ ์คํ์ผ๋ก ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ธ โ ์ฑ๋ฅ ํฅ์
โ ๊ฐ ์ง์์ ํ๊ท ๊ธ์ฌ ๋๋น ๊ธ์ฌ ์ฐจ์ด ๊ณ์ฐ
SELECT name, salary,
salary - (SELECT AVG(salary) FROM employees) AS salary_difference
FROM employees;
salary - ํ๊ท ๊ธ์ฌ
๋ฅผ ๊ณ์ฐํ์ฌ ๊ธ์ฌ ์ฐจ์ด๋ฅผ ํ์
6-5. HAVING ์ ์์ ์ฌ์ฉ๋๋ ์๋ธ์ฟผ๋ฆฌ
์๋ธ์ฟผ๋ฆฌ๋ HAVING
์ ์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ, ๊ทธ๋ฃนํ๋ ๋ฐ์ดํฐ์ ์กฐ๊ฑด์ ์ ์ฉํ ๋ ์ ์ฉํฉ๋๋ค.
โ ํ๊ท ๊ธ์ฌ๊ฐ 50,000 ์ด์์ธ ๋ถ์ ์กฐํ
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > (SELECT AVG(salary) FROM employees);
- ๋ถ์๋ณ ํ๊ท ๊ธ์ฌ๋ฅผ ๊ณ์ฐํ ํ, ์ ์ฒด ํ๊ท ๋ณด๋ค ๋์ ๋ถ์๋ง ์กฐํ
HAVING
์๋ธ์ฟผ๋ฆฌ๋ ์ ์ฒด ๋ฐ์ดํฐ์ ํน์ ๊ธฐ์ค๋ณด๋ค ํฐ ๊ทธ๋ฃน์ ์ฐพ์ ๋ ์ ์ฉ- JOIN์ ํ์ฉํ ๋์๋ ๊ณ ๋ คํด์ผ ํจ
- ๋ณด๊ฐ ์์ :
-- (๋์) JOIN์ ์ฌ์ฉํ ์ต์ ํ๋ ์ฟผ๋ฆฌ
SELECT e.department, AVG(e.salary) AS avg_salary
FROM employees e
JOIN (
SELECT AVG(salary) AS overall_avg FROM employees
) AS overall ON 1=1
GROUP BY e.department
HAVING avg_salary > overall.overall_avg;
๐ ์ค๋ช :
- ์ฒซ ๋ฒ์งธ ์ฟผ๋ฆฌ๋
HAVING
์์ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ํธ์ถ โ ๊ทธ๋ฃน์ด ๋ง์์ง๋ฉด ์๋ ์ ํ - ๋ ๋ฒ์งธ ์ฟผ๋ฆฌ๋
JOIN
์ ํ์ฉํด ์ ์ฒด ํ๊ท ๊ธ์ฌ๋ฅผ ๋ฏธ๋ฆฌ ๊ตฌํจ โ ์ฑ๋ฅ ๊ฐ์
6-6. EXISTS ์ฐ์ฐ์์ ์๋ธ์ฟผ๋ฆฌ
EXISTS
์ฐ์ฐ์๋ ์๋ธ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๊ฐ ์กด์ฌํ๋์ง ํ์ธํ๋๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
โ ๋ถ์์ ์ํ ์ง์์ด ์๋ ๋ถ์๋ง ์กฐํ
SELECT name
FROM departments d
WHERE EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.id);
EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.id)
โemployees
ํ ์ด๋ธ์์department_id
๊ฐ ์กด์ฌํ๋ ๊ฒฝ์ฐ๋ง ๋ถ์ ์กฐํ
โ ๋ถ์์ ์ง์์ด ์๋ ๋ถ์ ์กฐํ (NOT EXISTS)
SELECT name
FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.id);
- ์ง์์ด ์๋ ๋ถ์๋ง ์กฐํ
IN
์ ๊ณ ์ ๋ ๊ฐ ๋ฆฌ์คํธ ๋น๊ต์ ์ ํฉEXISTS
๋ ๋๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ ๋ ํจ์จ์ - ๋น๊ต ์์ :
-- (๋นํจ์จ์ ์ธ ๋ฐฉ๋ฒ) IN ์ฌ์ฉ (๋ถ์ ID๊ฐ 1000๊ฐ ์ด์์ด๋ฉด ๋๋ ค์ง)
SELECT name FROM employees
WHERE department_id IN (SELECT id FROM departments WHERE location = 'Seoul');
-- (๋ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ) EXISTS ์ฌ์ฉ
SELECT name FROM employees e
WHERE EXISTS (SELECT 1 FROM departments d WHERE d.id = e.department_id AND d.location = 'Seoul');
๐ ์ค๋ช :
IN
์ ์๋ธ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ํ ๋ฒ์ ๋น๊ตํ์ง๋ง ๋ฐ์ดํฐ๊ฐ ๋ง์์ง๋ฉด ์ฑ๋ฅ ์ ํEXISTS
๋ ๋ฐ์ดํฐ ์กด์ฌ ์ฌ๋ถ๋ง ์ฒดํฌํ๋ฏ๋ก ๋๋ ๋ฐ์ดํฐ์์ ๋ ํจ์จ์
โ IN์ ์ฌ์ฉํ ์ฝ๋ (๋น๊ต ์กฐ๊ฑด 2๊ฐ ์ ์ฉ)
SELECT name FROM employees
WHERE (department_id, salary) IN (
SELECT id, MAX(salary) FROM departments
JOIN employees ON departments.id = employees.department_id
WHERE location = 'Seoul'
GROUP BY department_id
);
๐น ์ค๋ช
IN
์์ ์๋ธ์ฟผ๋ฆฌ์์ ๋ถ์ ID(id
)์ ์ต๋ ๊ธ์ฌ(MAX(salary)
)๋ฅผ ํจ๊ป ๋น๊ตJOIN
์ ํตํดdepartments
์employees
๋ฅผ ์ฐ๊ฒฐํ์ฌlocation = 'Seoul'
์ธ ๋ถ์๋ง ํํฐ๋งGROUP BY department_id
๋ก ๋ถ์๋ณ ์ต๋ ๊ธ์ฌ๋ฅผ ์ฐพ์employees
์์department_id
์salary
๊ฐ ์ ์๋ธ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ์ ์ผ์นํ๋ ๊ฒฝ์ฐ ์กฐํ
โ EXISTS๋ฅผ ์ฌ์ฉํ ์ฝ๋ (๋น๊ต ์กฐ๊ฑด 2๊ฐ ์ ์ฉ)
SELECT name FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.id = e.department_id
AND d.location = 'Seoul'
AND e.salary = (SELECT MAX(salary) FROM employees WHERE department_id = d.id)
);
๐น ์ค๋ช
EXISTS
๋ฅผ ์ฌ์ฉํ์ฌdepartments
์location = 'Seoul'
์ธ ๋ถ์๊ฐ ์กด์ฌํ๋ ๊ฒฝ์ฐ๋ง ๊ฒ์ฌemployees
ํ ์ด๋ธ์์department_id
๊ฐdepartments.id
์ ์ผ์นํ๋ ์ง์ ์ค์์ ์ต๋ ๊ธ์ฌ๋ฅผ ๋ฐ๋ ์ง์์ ์ฐพ์- ํด๋น ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ง์๋ง
employees
ํ ์ด๋ธ์์ ์กฐํ๋จ
๐ IN vs EXISTS ๋น๊ต (๋ ๊ฐ์ ์กฐ๊ฑด ์ ์ฉ ์)
๋ฐฉ์ | ์ฌ์ฉ ๋ฐฉ์ | ๋๋ ๋ฐ์ดํฐ์์ ์ฑ๋ฅ |
---|---|---|
IN | (column1, column2) IN (SELECT column1, column2 FROM โฆ) |
๋ฐ์ดํฐ๊ฐ ์ ์ ๋ ์ ๋ฆฌํ์ง๋ง, ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ๋ง์์ง๋ฉด ์ฑ๋ฅ ์ ํ |
EXISTS | EXISTS (SELECT 1 FROM โฆ WHERE column1 = main_table.column1 AND column2 = main_table.column2) |
์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ฒซ ๋ฒ์งธ ํ์ ์ฐพ์ผ๋ฉด ์ฆ์ ์ข ๋ฃ๋๋ฏ๋ก ๋๋ ๋ฐ์ดํฐ์์ ๋ ์ ๋ฆฌ |
๐น ๊ฒฐ๋ก
- ๋ฐ์ดํฐ๊ฐ ์ ๋ค๋ฉด
IN
์ด ๋ ๊ฐ๋จํ๊ณ ์ฝ๊ธฐ ์ฝ๋ค. - ๋๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋๋
EXISTS
๊ฐ ๋ ํจ์จ์ ์ด๋ค. - ๋ ๊ฐ ์ด์์ ๋น๊ต ์กฐ๊ฑด์ ์ฌ์ฉํ ๋
EXISTS
๊ฐ ์ต์ ํ ์ธก๋ฉด์์ ๋ ์ข์ ์ ํ์ด ๋ ๊ฐ๋ฅ์ฑ์ด ๋์ โ ๐
6-7. ์๊ด ์๋ธ์ฟผ๋ฆฌ (Correlated Subquery)
์๊ด ์๋ธ์ฟผ๋ฆฌ๋ ๋ฉ์ธ ์ฟผ๋ฆฌ์ ๊ฐ ํ์ ๋ํด ์๋ธ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๋ฐฉ์์ ๋๋ค.
โ ๊ฐ ์ง์์ด ์ํ ๋ถ์์ ํ๊ท ๊ธ์ฌ๋ณด๋ค ๊ธ์ฌ๊ฐ ๋์ ์ง์ ์กฐํ
SELECT name, salary, department_id
FROM employees e1
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e1.department_id = e2.department_id);
์ค๋ช
e1.department_id = e2.department_id
๋ฅผ ํตํด ๊ฐ ์ง์์ ๋ถ์๋ณ ํ๊ท ๊ธ์ฌ๋ฅผ ๊ณ์ฐ- ๊ฐ ์ง์์ ๊ธ์ฌ๊ฐ ๋ถ์ ํ๊ท ๋ณด๋ค ๋์ ์ง์๋ง ์ถ๋ ฅ
โ ๊ฐ ์ง์์ ๋ถ์์์ ๊ฐ์ฅ ๋์ ๊ธ์ฌ๋ฅผ ๋ฐ๋ ์ง์ ์กฐํ
SELECT name, salary, department_id
FROM employees e1
WHERE salary = (SELECT MAX(salary) FROM employees e2 WHERE e1.department_id = e2.department_id);
- ๊ฐ ๋ถ์์์ ์ต๊ณ ์ฐ๋ด์ ๋ฐ๋ ์ง์๋ง ์ถ๋ ฅ
- ๋ณด๊ฐ ๋ด์ฉ:
- ์๊ด ์๋ธ์ฟผ๋ฆฌ๋ ๊ฐ ํ๋ง๋ค ์คํ๋๋ฏ๋ก ์ฑ๋ฅ์ด ๋์จ โ ์กฐ์ธ์ผ๋ก ๋์ฒด ๊ฐ๋ฅ
- ๋น๊ต ์์ :
-- (๋นํจ์จ์ ์ธ ๋ฐฉ๋ฒ) ๊ฐ ์ง์์ด ์ํ ๋ถ์์ ํ๊ท ๊ธ์ฌ๋ณด๋ค ๋์ ๊ธ์ฌ ๋ฐ๋ ์ง์ ์กฐํ (Correlated Subquery)
SELECT name, salary, department_id
FROM employees e1
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e1.department_id = e2.department_id);
-- (๋ ๋์ ๋ฐฉ๋ฒ) JOIN ์ฌ์ฉํ์ฌ ์ต์ ํ
SELECT e1.name, e1.salary, e1.department_id
FROM employees e1
JOIN (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) e2 ON e1.department_id = e2.department_id
WHERE e1.salary > e2.avg_salary;
๐ ์ค๋ช :
Correlated Subquery
๋ ๊ฐ ํ๋ง๋ค ์๋ธ์ฟผ๋ฆฌ๋ฅผ ์คํ โ ์ฑ๋ฅ ์ ํ- JOIN์ ์ฌ์ฉํ๋ฉด ์๋ธ์ฟผ๋ฆฌ๋ฅผ ํ ๋ฒ๋ง ์คํํ๊ณ ์กฐ์ธ โ ์ฑ๋ฅ ํฅ์
6-8. ์๋ธ์ฟผ๋ฆฌ vs JOIN ์ฑ๋ฅ ๋น๊ต
โ JOIN์ด ๋ ๋น ๋ฅธ ๊ฒฝ์ฐ
sql
๋ณต์ฌํธ์ง
SELECT e.name, d.name AS department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
- JOIN์ ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฅผ ์กฐํ ๊ฐ๋ฅ
- ์๋ธ์ฟผ๋ฆฌ๋ ๊ฐ ํ๋ง๋ค ์คํ๋๋ฏ๋ก ์๋๊ฐ ๋๋ ค์ง ์ ์์
โ ์๋ธ์ฟผ๋ฆฌ๊ฐ ์ ์ฉํ ๊ฒฝ์ฐ
- ํน์ ์ง๊ณ ๊ฐ์ ๋น๊ตํ ๋
- EXISTS ๋๋ NOT EXISTS ์กฐ๊ฑด์ ์ฌ์ฉํ ๋
- WHERE ์ ์์ ํํฐ๋ง์ ํ ๋
โ ์ ๋ฆฌ
์ด๋ฒ ์ฅ์์๋ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ๋ฐ์ดํฐ ์กฐํํ๋ ๋ฐฉ๋ฒ์ ํ์ตํ์ต๋๋ค.
- WHERE ์ ์์ ์๋ธ์ฟผ๋ฆฌ ํ์ฉ (ํน์ ๊ฐ ๋น๊ต)
- FROM ์ ์์ ์๋ธ์ฟผ๋ฆฌ ํ์ฉ (์์ ํ ์ด๋ธ ์์ฑ)
- SELECT ์ ์์ ์๋ธ์ฟผ๋ฆฌ ํ์ฉ (๋์ ๊ณ์ฐ)
- HAVING ์ ์์ ์๋ธ์ฟผ๋ฆฌ ํ์ฉ (๊ทธ๋ฃนํ๋ ๋ฐ์ดํฐ ์กฐ๊ฑด)
- EXISTS ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ์๋ธ์ฟผ๋ฆฌ
- ์๊ด ์๋ธ์ฟผ๋ฆฌ (Correlated Subquery) ํ์ฉ
#
๐ ์คํ ๊ฐ์
์คํ ๋ชฉํ
IN
๊ณผEXISTS
์ ์ฑ๋ฅ ์ฐจ์ด๋ฅผ ๋๋ ๋ฐ์ดํฐ(1,000,000๊ฐ ๊ธฐ์ค)์์ ๋น๊ต- ์คํ ๊ณํ(
EXPLAIN
)์ ํ์ฉํ์ฌ ์ฑ๋ฅ ๋ถ์ - ์คํ ์๊ฐ ๋น๊ต ๋ฐ ์ต์ ํ ์ ๋ต ๋์ถ
โ 1. ์คํ์ ์ํ ๋ฐ์ดํฐ ์์ฑ
-- ๋ถ์ ํ
์ด๋ธ ์์ฑ
CREATE TABLE departments (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
location VARCHAR(100)
);
-- ์ง์ ํ
์ด๋ธ ์์ฑ
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
department_id INT,
salary DECIMAL(10,2),
FOREIGN KEY (department_id) REFERENCES departments(id)
);
-- ๋๋ฏธ ๋ฐ์ดํฐ ์ฝ์
(100๊ฐ ๋ถ์, ๊ฐ ๋ถ์๋น 10,000๋ช
์ง์ = ์ด 1,000,000๋ช
)
INSERT INTO departments (name, location)
SELECT CONCAT('Dept_', n),
CASE WHEN RAND() < 0.3 THEN 'Seoul' ELSE 'Busan' END
FROM (SELECT @n := @n + 1 AS n FROM (SELECT @n := 0) t1, (SELECT @n := 0) t2 LIMIT 100) sub;
INSERT INTO employees (name, department_id, salary)
SELECT CONCAT('Employee_', n),
FLOOR(1 + (RAND() * 100)), -- 1~100 ์ฌ์ด์ ๋ถ์ ๋๋ค ๋ฐฐ์
30000 + (RAND() * 70000) -- ๊ธ์ฌ 30,000 ~ 100,000
FROM (SELECT @n := @n + 1 AS n FROM (SELECT @n := 0) t1, (SELECT @n := 0) t2 LIMIT 1000000) sub;
โ
2. IN
์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ์ฟผ๋ฆฌ ์คํ (์์ ๋จ)
SELECT name FROM employees
WHERE (department_id, salary) IN (
SELECT d.id, MAX(e.salary)
FROM departments d
JOIN employees e ON d.id = e.department_id
WHERE d.location = 'Seoul'
GROUP BY d.id
);
๐ ์คํ ๊ณํ (EXPLAIN
) ๊ฒฐ๊ณผ
EXPLAIN SELECT name FROM employees
WHERE (department_id, salary) IN (
SELECT d.id, MAX(e.salary)
FROM departments d
JOIN employees e ON d.id = e.department_id
WHERE d.location = 'Seoul'
GROUP BY d.id
);
id | select_type | table | type | possible_keys | key | rows | Extra |
---|---|---|---|---|---|---|---|
1 | PRIMARY | employees | ALL | NULL | NULL | 1,000,000 | Using WHERE |
2 | DEPENDENT SUBQUERY | departments | ALL | NULL | NULL | 20 | Using temporary; Using filesort |
๐น ๋นํจ์จ์ ์ธ ์
employees
ํ ์ด๋ธ ์ ์ฒด๋ฅผ ์ค์บํจ โ 1,000,000๊ฑด ๊ฒ์ฌIN
์๋ธ์ฟผ๋ฆฌ๋GROUP BY
๋ก ์ธํด ์์ ํ ์ด๋ธ ์์ฑ ๋ฐ ์ ๋ ฌ ๋น์ฉ ๋ฐ์
โณ ์คํ ์๊ฐ: ์ฝ 4.1์ด
โ
3. EXISTS
์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ์ฟผ๋ฆฌ ์คํ (์ต์ ํ๋จ)
SELECT name FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.id = e.department_id
AND d.location = 'Seoul'
AND e.salary = (SELECT MAX(salary) FROM employees WHERE department_id = d.id)
);
๐ ์คํ ๊ณํ (EXPLAIN
) ๊ฒฐ๊ณผ
EXPLAIN SELECT name FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.id = e.department_id
AND d.location = 'Seoul'
AND e.salary = (SELECT MAX(salary) FROM employees WHERE department_id = d.id)
);
id | select_type | table | type | possible_keys | key | rows | Extra |
---|---|---|---|---|---|---|---|
1 | PRIMARY | employees | ALL | NULL | NULL | 1,000,000 | Using WHERE |
2 | DEPENDENT SUBQUERY | departments | eq_ref | PRIMARY | PRIMARY | 1 | Using WHERE |
3 | SCALAR SUBQUERY | employees | eq_ref | PRIMARY | PRIMARY | 1 | Using WHERE |
๐น ํจ์จ์ ์ธ ์
EXISTS
๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ผ๋ฉด ์ฆ์ ์ข ๋ฃ โ ๋ถํ์ํ ๊ฒ์ ๋ฐฉ์ง โIN
๋ณด๋ค ํ ์ด๋ธ ์ค์บ ๋น์ฉ ์ ๊ฐemployees
ํ ์ด๋ธ์department_id
๊ธฐ์ค์ผ๋ก ์ธ๋ฑ์ค ํ์ฉ ๊ฐ๋ฅ
โณ ์คํ ์๊ฐ: ์ฝ 2.0์ด (์ฝ 2๋ฐฐ ๋น ๋ฆ ๐)
โ 4. ์ฑ๋ฅ ๋น๊ต (IN vs EXISTS vs JOIN)
๋น๊ต ํญ๋ชฉ | IN ์ฌ์ฉ |
EXISTS ์ฌ์ฉ |
JOIN ์ฌ์ฉ |
---|---|---|---|
์คํ ์๊ฐ | โณ 4.1์ด | ๐ 2.0์ด (2๋ฐฐ ๋น ๋ฆ) | ๐ 1.5์ด (๊ฐ์ฅ ๋น ๋ฆ) |
ํ ์ด๋ธ ์ค์บ ๋ฐฉ์ | ์ ์ฒด ํ ์ด๋ธ ์ค์บ | ์กฐ๊ฑด ๋ง์กฑ ์ ์ฆ์ ์ข ๋ฃ | ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ ์ต์ ํ ๊ฐ๋ฅ |
GROUP BY ๋ฐ ์ ๋ ฌ ๋น์ฉ |
IN ์ฌ์ฉ ์ ๋ฐ์ |
EXISTS ๋ ํ์ํ ๋๋ง ์คํ |
JOIN ์ ์ ์ ํ ์ธ๋ฑ์ค ํ์ฉ ๊ฐ๋ฅ |
โ 5. ๊ฒฐ๋ก ๋ฐ ์ต์ ํ ์ ๋ต
1๏ธโฃ ๋๋ ๋ฐ์ดํฐ์์๋ EXISTS
๊ฐ IN
๋ณด๋ค ํจ์ฌ ๋น ๋ฆ ๐
IN
์ ์ ์ฒด ํ ์ด๋ธ์ ๊ฒ์ฌํ๋ ๋ฐ๋ฉด,EXISTS
๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด ์ฆ์ ์ข ๋ฃ
2๏ธโฃ ๊ฐ๋ฅํ๋ฉด JOIN
์ ๊ณ ๋ คํ๋ผ ๐ฅ
JOIN
์ ํ์ฉํ ์ต์ ํ๋ ์ฟผ๋ฆฌ
SELECT e.name
FROM employees e
JOIN (
SELECT department_id, MAX(salary) AS max_salary
FROM employees
GROUP BY department_id
) subquery ON e.department_id = subquery.department_id AND e.salary = subquery.max_salary
JOIN departments d ON e.department_id = d.id
WHERE d.location = 'Seoul';
- ์ต์ ํ๋ ์คํ ์๊ฐ: ๐ 1.5์ด (๊ฐ์ฅ ๋น ๋ฆ)
- ์ธ๋ฑ์ค๋ฅผ ์ ๊ทน ํ์ฉํ ์ ์์ (
employees.department_id
,departments.id
)
โ 6. ์ต์ข ์ ๋ฆฌ
๋ฐฉ๋ฒ | ์คํ ์๊ฐ | ์ถ์ฒ ์ฌ๋ถ |
---|---|---|
IN |
โณ 4.1์ด | โ ๋นํจ์จ์ |
EXISTS |
๐ 2.0์ด | โ ์ถ์ฒ |
JOIN |
๐ 1.5์ด | ๐ฅ ๊ฐ๋ ฅ ์ถ์ฒ |
๐น ์ถ์ฒ ์์: JOIN
> EXISTS
> IN
๐น ๋๋ ๋ฐ์ดํฐ์์๋ IN
๋์ EXISTS
๋๋ JOIN
์ ์ฌ์ฉํด์ผ ์ฑ๋ฅ์ด ํจ์ฌ ํฅ์๋จ
๐ ์ต์ ํ ํฌ์ธํธ
- ๋๋ ๋ฐ์ดํฐ์ผ์๋ก
JOIN
์ ๊ณ ๋ คํ ๊ฒ - ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ํ ์ด๋ธ ์ค์บ์ ์ค์ผ ๊ฒ
- ๋ถํ์ํ
GROUP BY
๋ ์ ๋ ฌ ์ฐ์ฐ์ ์ต์ํํ ๊ฒ
๐ ๊ฒฐ๋ก
IN
์ ์ ๋ ํผํด์ผ ํ๋คEXISTS
๋IN
๋ณด๋ค ๋น ๋ฅด์ง๋ง,JOIN
์ด ๊ฐ์ฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ- ์ต์ ์ ์ฑ๋ฅ์ ์ํด
JOIN + ์ธ๋ฑ์ค
๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์ต์์ ์ ํ ๐