SQL에서 JOIN과 서브쿼리는 데이터를 결합하고 분석하는 데 중요한 역할을 한다. JOIN을 사용하면 여러 테이블에서 관련 데이터를 연결할 수 있고, 서브쿼리를 통해 복잡한 조건을 효율적으로 처리할 수 있다. 이번 포스팅에서는 JOIN의 다양한 종류와 서브쿼리의 활용법에 대해 예제를 통해 알아본다.
목차
1. 조인(JOIN)
SQL에서 JOIN
은 두 개 이상의 테이블을 결합하여 데이터를 조회하는 핵심적인 명령어이다. JOIN
을 사용하면 여러 테이블에서 관련된 데이터를 하나로 합쳐 유용한 정보를 추출할 수 있다. 관계형 데이터베이스는 데이터를 여러 테이블에 분리하여 저장하는데, 실질적으로 필요한 정보는 여러 테이블에 걸쳐 있기 때문에 JOIN
을 사용하여 데이터를 결합해야 한다.
JOIN
유형은 각기 다른 방식으로 데이터를 결합한다. 이번에는 가장 많이 사용되는 INNER JOIN
, LEFT JOIN
, RIGHT JOIN
, FULL OUTER JOIN
, CROSS JOIN
을 설명하겠다.
기본 테이블 구조
먼저, users
와 orders
라는 두 테이블을 가정한다. users
테이블은 사용자 정보를 저장하며, orders
테이블은 주문 정보를 저장한다. 각 테이블은 서로 외래 키를 사용하여 연결된다.
users 테이블
CREATE TABLE users (
id INT PRIMARY KEY, -- 사용자 고유 ID
name VARCHAR(100), -- 사용자 이름
email VARCHAR(100) -- 사용자 이메일
);
-- 예시 데이터
-- id | name | email
-- 1 | John Doe | john@example.com
-- 2 | Jane Smith | jane@example.com
-- 3 | Bob Brown | bob@example.com
-- 4 | Alice Lee | alice@example.com
orders 테이블
CREATE TABLE orders (
id INT PRIMARY KEY, -- 주문 고유 ID
user_id INT, -- 사용자 ID (users 테이블과 연결)
amount DECIMAL(10, 2), -- 주문 금액
order_date DATE, -- 주문 날짜
FOREIGN KEY (user_id) REFERENCES users(id) -- 외래 키 관계 설정
);
-- 예시 데이터
-- id | user_id | amount | order_date
-- 1 | 1 | 100.00 | 2025-03-01
-- 2 | 2 | 150.00 | 2025-03-02
-- 3 | 1 | 200.00 | 2025-03-03
-- 4 | 3 | 50.00 | 2025-03-04
-- 5 | 1 | 75.00 | 2025-03-05
1.1 INNER JOIN
INNER JOIN
은 두 테이블 간의 교집합을 반환한다. 즉, 두 테이블에서 공통된 데이터만 결합하여 출력된다. INNER JOIN
을 사용하면, 두 테이블에서 일치하는 행만 결과로 반환된다. 만약 두 테이블 중 하나라도 일치하는 데이터가 없다면 그 행은 결과에 포함되지 않는다.
SELECT users.name, orders.amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;
이 쿼리는 users
테이블과 orders
테이블을 user_id
를 기준으로 결합하여, 두 테이블에서 일치하는 데이터만 출력한다.
결과:
name | amount |
John Doe | 100.00 |
Jane Smith | 150.00 |
John Doe | 200.00 |
Bob Brown | 50.00 |
John Doe | 75.00 |
위 결과에서 알 수 있듯이, INNER JOIN
은 주문 내역이 있는 사용자의 데이터만 반환한다. 주문 내역이 없는 사용자는 결과에서 제외된다.
1.2 LEFT JOIN
LEFT JOIN
은 왼쪽 테이블의 모든 행을 포함하고, 오른쪽 테이블에서 일치하는 데이터를 결합하여 반환한다. 만약 오른쪽 테이블에 일치하는 데이터가 없다면 해당 칼럼은 NULL로 반환된다. 즉, 왼쪽 테이블의 모든 데이터는 결과에 포함되며, 오른쪽 테이블에서 일치하는 데이터가 없으면 NULL로 표시된다.
SELECT users.name, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
이 쿼리는 users
테이블의 모든 사용자와 그에 해당하는 주문 내역을 반환한다. 주문 내역이 없는 사용자는 `amount`가 NULL로 표시된다.
결과:
name | amount |
John Doe | 100.00 |
Jane Smith | 150.00 |
John Doe | 200.00 |
Bob Brown | 50.00 |
John Doe | 75.00 |
Alice Lee | NULL |
1.3 RIGHT JOIN
RIGHT JOIN
은 오른쪽 테이블의 모든 행을 포함하고, 왼쪽 테이블에서 일치하는 데이터를 결합하여 반환한다. 만약 왼쪽 테이블에 일치하는 데이터가 없다면 해당 칼럼은 NULL로 반환된다.
SELECT users.name, orders.amount
FROM users
RIGHT JOIN orders ON users.id = orders.user_id;
이 쿼리는 모든 주문 내역을 포함하고, 그에 해당하는 사용자 정보를 결합한다. 만약 사용자 정보가 없는 주문은 `name`이 NULL로 표시된다.
결과:
name | amount |
John Doe | 100.00 |
Jane Smith | 150.00 |
John Doe | 200.00 |
Bob Brown | 50.00 |
John Doe | 75.00 |
NULL | 300.00 |
1.4 FULL OUTER JOIN
FULL OUTER JOIN
은 양쪽 테이블의 모든 행을 반환하며, 일치하는 데이터가 없으면 NULL로 표시된다. MySQL에서는 FULL OUTER JOIN
을 직접 지원하지 않지만, UNION
을 사용하여 대체할 수 있다.
SELECT users.name, orders.amount
FROM users
FULL OUTER JOIN orders ON users.id = orders.user_id;
이 쿼리는 두 테이블의 모든 데이터를 결합하고, 일치하지 않는 데이터는 NULL로 표시된다. `UNION`을 사용하여 MySQL에서 이 기능을 구현할 수 있다.
결과:
name | amount |
John Doe | 100.00 |
Jane Smith | 150.00 |
John Doe | 200.00 |
Bob Brown | 50.00 |
John Doe | 75.00 |
NULL | 300.00 |
Alice Lee | NULL |
1.5 CROSS JOIN
CROSS JOIN은 두 테이블의 모든 가능한 조합을 반환한다. 즉, 카르테시안 곱을 계산하여 모든 가능한 데이터 조합을 생성한다. 이 방식은 보통 사용되지 않으며, 특정 상황에서만 필요하다. 예를 들어, 두 개의 목록에서 가능한 모든 조합을 만들고자 할 때 사용될 수 있다.
CROSS JOIN의 중요한 특징은 두 테이블의 행 수가 곱해져서 결과가 행 수가 급격히 많아질 수 있다는 점이다. 예를 들어, 왼쪽 테이블에 5개의 행, 오른쪽 테이블에 3개의 행이 있을 경우, 5 x 3 = 15개의 행이 결과로 반환된다. 일반적으로 JOIN은 두 테이블에서 관계를 찾기 위해 사용되지만, CROSS JOIN은 모든 조합을 만들어내는 것이 목적이기 때문에 두 테이블 간의 관계가 없을 때 사용된다.
예시 쿼리:
SELECT users.name, orders.amount
FROM users
CROSS JOIN orders;
이 쿼리는 users
테이블의 모든 사용자와 orders
테이블의 모든 주문을 모든 가능한 조합으로 결합한다. 두 테이블의 각 행을 서로 곱하여 모든 조합을 생성한다. 결과적으로 각 사용자의 주문 내역이 각각 모든 다른 사용자의 주문 내역과 결합된다.
예시 데이터:
users 테이블:
id | name |
1 | John Doe |
2 | Jane Smith |
orders 테이블:
id | amount |
1 | 100.00 |
2 | 150.00 |
결과:
name | amount |
John Doe | 100.00 |
John Doe | 150.00 |
Jane Smith | 100.00 |
Jane Smith | 150.00 |
위의 결과에서 볼 수 있듯이, 각 사용자가 각 주문과 결합되어 모든 가능한 조합이 생성된다. John Doe는 100.00과 150.00의 두 주문을 하고, Jane Smith도 100.00과 150.00의 두 주문을 받는다.
CROSS JOIN은 두 테이블 간의 관계가 필요 없는 경우 유용하지만, 대규모 데이터셋에 대해서는 효율적이지 않다. 왜냐하면 두 테이블의 행 수가 곱해져서 결과의 크기가 급격히 커지기 때문이다. 이로 인해, 너무 많은 데이터를 반환하는 경우 성능 문제가 발생할 수 있다.
2. 서브쿼리(Subquery)
서브쿼리는 쿼리 내에 포함된 또 다른 쿼리이다. 서브쿼리는 하나의 쿼리문 안에서 다른 쿼리문을 실행하여, 그 결과를 외부 쿼리에서 조건으로 사용하거나 결과를 필터링하는 데 활용된다. 서브쿼리는 복잡한 데이터 조건을 처리하거나, 하나의 쿼리문으로 처리하기 어려운 경우에 매우 유용하다. 서브쿼리는 SELECT, INSERT, UPDATE, DELETE 등의 SQL 문에서 사용될 수 있으며, 외부 쿼리의 조건절, 칼럼값, 혹은 다른 데이터를 동적으로 생성하는 데 사용된다.
서브쿼리는 내부 쿼리가 먼저 실행되고, 그 결과가 외부 쿼리로 전달되는 방식으로 작동한다. 서브쿼리를 적절히 사용하면, 복잡한 조건을 처리하거나, 계산된 값을 외부 쿼리에서 바로 사용할 수 있다.
2.1 서브쿼리의 기본 개념
서브쿼리는 보통 괄호 () 안에 작성되며, 외부 쿼리에서 참조하는 방식으로 사용된다. 서브쿼리는 단일값을 반환할 수도 있고, 여러 값을 반환할 수도 있다. 서브쿼리를 사용하는 위치에 따라 결과가 다르게 나타날 수 있기 때문에, 그 사용법에 대해 명확히 이해하고 있어야 한다.
2.2 WHERE 절
WHERE 절 내에서 서브쿼리는 조건을 추가하거나 데이터를 필터링하는 데 사용된다. 주로 IN, EXISTS, ANY, ALL 등의 키워드와 함께 사용되어, 다른 테이블에서 조건을 추출하여 현재 쿼리에서 비교할 수 있다.
예시: IN을 사용하는 서브쿼리
SELECT name
FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 500);
이 쿼리는 orders 테이블에서 500원 이상의 주문을 한 사용자의 이름을 조회하는 서브쿼리이다. 서브쿼리는 orders 테이블에서 amount가 500보다 큰 user_id를 조회하고, 그 값을 users 테이블의 id와 비교하여 일치하는 사용자 이름을 반환한다.
결과:
name |
John Doe |
Jane Smith |
2.3 SELECT 절
서브쿼리는 SELECT 절 내에서도 사용될 수 있다. 서브쿼리가 결과 칼럼으로 사용되어, 쿼리의 칼럼 값을 계산하거나, 다른 테이블에서 값을 가져오는 데 사용된다.
예시: SELECT 내 서브쿼리
SELECT name,
(SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS order_count
FROM users;
이 쿼리는 각 사용자에 대해 주문 건수를 계산하여, order_count라는 새 칼럼에 그 값을 표시한다. 서브쿼리는 users 테이블의 각 user_id에 대해 orders 테이블에서 해당 사용자의 주문 수를 COUNT(*)를 사용해 반환한다.
결과:
name | order_count |
John Doe | 5 |
Jane Smith | 3 |
2.4 FROM 절 (인라인 뷰)
서브쿼리는 FROM 절에서 사용할 수 있으며, 이때 서브쿼리는 인라인 뷰(Inline View)라고 불린다. 인라인 뷰는 서브쿼리 결과를 마치 테이블처럼 다루는 방식이다. 즉, 서브쿼리가 임시 테이블처럼 작동하며, 외부 쿼리에서 이 임시 테이블을 조인하거나 필터링하는 데 사용할 수 있다.
FROM 절에서 서브쿼리를 사용하는 이유는 복잡한 데이터 집계나 필터링을 하위 쿼리로 미리 처리하고, 그 결과를 외부 쿼리에서 활용할 수 있기 때문이다. 인라인 뷰는 복잡한 연산을 미리 계산하여 효율적으로 데이터를 처리하는 데 유용하다.
예시 1: 부서별 평균 연봉 구하기
employees 테이블에는 직원의 정보와 연봉 정보가 있다. 연봉이 40,000 이상인 직원만 대상으로, 각 부서별 평균 연봉을 구하려 한다. employees 테이블에서 먼저 연봉 40,000 이상인 직원들만 추출한 뒤, 그 데이터를 부서별로 그룹화하여 평균을 구하는 방식이다.
SELECT department, AVG(salary)
FROM (
SELECT department, salary
FROM employees
WHERE salary > 40000
) AS high_salary
GROUP BY department;
- 서브쿼리: 내부 쿼리는 employees 테이블에서 연봉이 40,000 이상인 직원만 추출한다.
- 외부 쿼리: 외부 쿼리에서는 서브쿼리의 결과를 high_salary라는 임시 테이블로 사용하고, 그 결과에서 부서별 평균 연봉을 구한다.
- GROUP BY: 부서별로 그룹화하고, AVG(salary)를 사용하여 각 부서의 평균 연봉을 계산한다.
결과:
department | avg_salary |
HR | 48000 |
IT | 55000 |
Sales | 42000 |
위 결과에서 각 부서별로 연봉이 40,000 이상인 직원들만 대상으로 평균 연봉을 계산한 값을 확인할 수 있다. 서브쿼리를 사용하여 연봉 조건을 먼저 필터링하고, 그 결과를 외부 쿼리에서 부서별로 집계하는 방식이다.
예시 2: 상위 5명의 고객의 총 주문 금액 구하기
이번에는 고객별로 총 주문 금액을 계산하고, 그중에서 총 주문 금액이 가장 큰 상위 5명을 구하는 예제를 살펴보겠다. 상위 5명의 고객을 구하기 위해 서브쿼리를 사용하여 총 주문 금액을 먼저 계산하고, 그 결과를 외부 쿼리에서 정렬하여 상위 5명을 추출하는 방식이다.
SELECT customer_name, total_amount
FROM (
SELECT customers.name AS customer_name, SUM(orders.amount) AS total_amount
FROM customers
INNER JOIN orders ON customers.id = orders.customer_id
GROUP BY customers.id
) AS total_orders
ORDER BY total_amount DESC
LIMIT 5;
- 서브쿼리: 서브쿼리에서는 customers 테이블과 orders 테이블을 JOIN하여 고객별 총 주문 금액을 계산한다.
- 외부 쿼리: 외부 쿼리에서는 서브쿼리의 결과를 total_orders라는 임시 테이블처럼 사용하고, ORDER BY를 통해 총 주문 금액을 내림차순으로 정렬한 후, 상위 5명을 LIMIT으로 제한한다.
예시 결과:
customer_name | total_amount |
John Doe | 1500.00 |
Jane Smith | 1200.00 |
Alice Lee | 1100.00 |
Bob Brown | 900.00 |
Charlie Lee | 850.00 |
위 결과에서 총 주문 금액이 큰 상위 5명의 고객을 확인할 수 있다. 서브쿼리를 사용하여 고객별 총 주문 금액을 먼저 계산하고, 그 결과를 외부 쿼리에서 정렬하여 상위 5명을 추출하는 방식이다.
FROM 절에서 서브쿼리(인라인 뷰)는 복잡한 연산을 미리 처리하고, 그 결과를 외부 쿼리에서 활용할 수 있게 해 준다. 서브쿼리로 먼저 데이터를 필터링하거나 집계하고, 그 결과를 외부 쿼리에서 그룹화, 정렬, 추가적인 필터링을 통해 처리할 수 있다. 이 방식은 쿼리 가독성을 높이고, 성능을 개선하는 데 유용하다.
2.5 IN vs EXISTS
서브쿼리에서 IN과 EXISTS는 조건을 비교하는 데 사용되는 두 가지 주요 방식이다. 두 키워드는 비슷한 기능을 수행하지만, 그 사용 시기와 성능 차이가 있을 수 있다.
1. IN
IN은 서브쿼리가 반환하는 값 전체와 비교할 때 사용된다. 서브쿼리가 반환하는 데이터셋이 작은 경우에 유리하며, 하위 쿼리에서 반환되는 값들이 명확하게 정해진 목록일 때 사용한다. IN은 주로 간단한 조건이나 목록 비교에 사용된다.
예시: 특정 고객이 주문한 제품이 주어진 가격 목록에 포함되는지 확인하려는 경우, IN을 사용할 수 있다.
SELECT product_name
FROM products
WHERE price IN (SELECT price FROM orders WHERE customer_id = 1);
위 쿼리는 고객 1번이 주문한 제품 가격 중, 주어진 가격 목록에 포함된 제품을 찾아낸다. 서브쿼리에서 가격을 반환하고, IN을 사용하여 제품 가격이 그 목록에 포함되는지 확인한다.
2. EXISTS
EXISTS는 서브쿼리가 행의 존재 여부만 확인할 때 사용된다. 서브쿼리 내에서 데이터가 존재하는지 여부만 체크하고, 해당 데이터가 얼마나 많은지는 중요하지 않다. 대용량 데이터셋을 처리할 때 성능상 유리할 수 있다. EXISTS는 하위 쿼리에서 조건을 만족하는 행이 존재하는지만 확인하므로 성능이 더 우수한 경우가 많다.
예시: 특정 고객이 주문을 한 적이 있는지 확인하려는 경우, EXISTS를 사용할 수 있다.
SELECT name
FROM customers
WHERE EXISTS (SELECT 1 FROM orders WHERE orders.customer_id = customers.id);
위 쿼리는 고객별로 주문이 존재하는지 확인한다. 서브쿼리는 고객이 주문한 내역이 존재하는지 여부만 확인하고, 존재하면 해당 고객의 이름을 반환한다. EXISTS는 조건을 만족하는 행이 존재하는지 확인하므로 주문이 있는 고객만 조회할 수 있다. 행이 존재하는지만 확인하기 때문에 일반적으로 SELECT 1을 사용한다.
IN과 EXISTS의 성능 차이
IN은 서브쿼리가 반환하는 모든 값과 비교하기 때문에, 데이터가 적은 경우나 값이 확정적인 경우에 적합하다. 하지만 데이터가 많을 때는 서브쿼리 결과의 크기가 커지면 성능이 떨어질 수 있다.
EXISTS는 조건을 만족하는 행이 존재하는지 여부만 확인하므로, 대규모 데이터셋에서 성능이 더 좋을 수 있다. 예를 들어, 두 테이블 간의 관계가 복잡하거나 서브쿼리의 결과가 큰 경우에는 EXISTS가 더 효율적일 수 있다.
3. IN vs EXISTS 사용 예시 비교
다음은 IN과 EXISTS를 사용한 두 쿼리 예제이다. 동일한 조건에서 IN과 EXISTS의 차이를 비교할 수 있다.
IN 사용 예시:
SELECT name
FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE order_date > '2025-01-01');
EXISTS 사용 예시:
SELECT name
FROM customers
WHERE EXISTS (SELECT 1 FROM orders WHERE orders.customer_id = customers.id AND orders.order_date > '2025-01-01');
두 쿼리 모두 주문일자가 2025년 1월 1일 이후인 고객을 찾는 쿼리이다. IN은 서브쿼리에서 모든 customer_id 값을 반환하고, EXISTS는 주문이 존재하는지 여부만 확인한다.
차이점: IN은 서브쿼리가 반환하는 값들과 모두 비교하며, EXISTS는 조건을 만족하는 행의 존재 여부만 확인한다. 따라서, 데이터셋이 커질수록 EXISTS의 성능이 우수한 경우가 많다.
정리하면, IN과 EXISTS는 서브쿼리에서 데이터를 비교하는 방법으로 사용된다. IN은 서브쿼리가 반환하는 값들과 모두 비교하는 방식이고, EXISTS는 행의 존재 여부만 확인한다. 각 방법은 성능과 데이터의 특성에 따라 다르게 선택할 수 있으며, IN은 작은 데이터셋이나 목록 비교에 유리하고, EXISTS는 대규모 데이터셋에서 성능을 개선할 수 있다.
3. 조인과 서브쿼리의 활용
JOIN과 서브쿼리는 SQL에서 데이터베이스의 복잡한 데이터 구조를 처리하는 데 유용한 도구들이다. 두 가지 기법을 결합하면, 더 복잡한 데이터 관계를 해결하거나, 조건을 세밀하게 필터링하는 데 큰 도움이 된다. JOIN을 사용하여 여러 테이블을 결합하고, 그 결과를 서브쿼리로 필터링하거나 추가 조건을 적용하는 방식은 매우 강력한 조합이다.
이번 섹션에서는 JOIN과 서브쿼리를 함께 사용하는 실용적인 예제를 다룬다. 이렇게 결합된 쿼리는 다양한 조건을 만족하는 데이터를 추출하거나, 복잡한 데이터 분석을 처리하는 데 유용하다.
3.1 예제 1
이 예제에서는 JOIN과 서브쿼리를 결합하여, 최근 주문이 있는 고객을 찾아낸 후, 그들의 평균 주문 금액을 계산하여, 상위 5명의 고객을 조회하는 쿼리를 작성한다.
- 고객의 정보가 users 테이블에 저장되어 있고, 주문 내역은 orders 테이블에 저장되어 있다.
- 각 고객은 여러 번 주문할 수 있으며, 우리는 최근에 주문한 고객의 평균 주문 금액을 구하고, 이들 중 평균 금액이 가장 높은 상위 5명을 조회하고자 한다.
예시 쿼리:
SELECT users.name, AVG(orders.amount) AS avg_amount
FROM users
INNER JOIN orders ON users.id = orders.user_id
WHERE orders.order_date = (
SELECT MAX(order_date)
FROM orders
WHERE orders.user_id = users.id
)
GROUP BY users.id
ORDER BY avg_amount DESC
LIMIT 5;
- INNER JOIN: users 테이블과 orders 테이블을 user_id를 기준으로 결합하여, 각 사용자의 주문 내역을 가져온다.
- 서브쿼리: 서브쿼리는 orders 테이블에서 각 고객의 가장 최근 주문 날짜를 조회한다. MAX(order_date)는 각 사용자별로 가장 최근에 주문한 날짜를 반환한다.
- WHERE 절: 서브쿼리에서 반환된 최근 주문 날짜와 일치하는 주문만 필터링하여, 각 고객의 최근 주문 내역만을 고려한다.
- GROUP BY: users.id를 기준으로 각 고객의 주문 금액 평균을 계산한다.
- ORDER BY: avg_amount를 기준으로 내림차순 정렬하여, 평균 주문 금액이 가장 높은 고객부터 조회한다.
- LIMIT 5: 상위 5명만 출력하도록 제한한다.
결과 예시:
name | avg_amount |
Alice Lee | 350 |
John Doe | 300 |
Jane Smith | 250 |
Bob Johnson | 200 |
Charlie Lee | 180 |
이 결과에서는 최근 주문을 한 상위 5명의 고객과 그들의 평균 주문 금액을 확인할 수 있다. 서브쿼리를 사용하여 각 고객의 가장 최근 주문 날짜를 확인하고, 이를 INNER JOIN과 결합하여 최근 주문을 기준으로 평균 금액을 구할 수 있다.
3.2 예제 2
이번 예제에서는 JOIN과 서브쿼리를 결합하여, 특정 카테고리에 속하는 가장 많이 주문된 상위 3개 제품과 그 주문 수를 조회한다. 이 예제는 products와 orders 테이블을 결합하여, 주문 내역을 기반으로 제품별 주문 수를 계산하는 것이다.
- products 테이블에는 제품 정보와 해당 카테고리가 저장되어 있고, orders 테이블에는 각 주문이 기록되어 있다.
- 우리는 특정 카테고리에 속하는 제품 중, 가장 많이 주문된 상위 3개 제품과 그 주문 수를 조회하려 한다.
예시 쿼리:
SELECT products.name, COUNT(orders.id) AS order_count
FROM products
INNER JOIN orders ON products.id = orders.product_id
WHERE products.category = 'Electronics'
GROUP BY products.id
ORDER BY order_count DESC
LIMIT 3;
- INNER JOIN: products 테이블과 orders 테이블을 product_id를 기준으로 결합하여, 각 제품의 주문 내역을 가져온다.
- WHERE 절: products.category 칼럼을 사용하여, 'Electronics' 카테고리에 속하는 제품들만 필터링한다.
- GROUP BY: products.id를 기준으로 각 제품에 대해 주문 수를 COUNT() 함수로 계산한다.
- ORDER BY: order_count를 기준으로 내림차순 정렬하여, 주문 수가 가장 많은 제품부터 조회한다.
- LIMIT 3: 주문 수가 가장 많은 상위 3개 제품만 출력하도록 제한한다.
결과 예시:
name | order_count |
Smartphone A | 120 |
Laptop B | 90 |
Headphones C | 85 |
이 결과는 Electronics 카테고리에서 가장 많이 주문된 상위 3개 제품과 그들의 주문 수를 보여준다. JOIN을 사용하여 제품과 주문 데이터를 결합하고, 서브쿼리를 사용하여 특정 카테고리에 속하는 제품을 선택한 후, 주문 수를 계산한다.
3.3 예제 3
이번 예제에서는 특정 고객이 주문한 상품들 중 가장 비싼 상품을 찾는 쿼리를 작성한다. 이 예제는 users, orders, products 테이블을 결합하여, 고객별로 가장 비싼 상품을 조회한다.
- users 테이블에는 고객 정보가, orders 테이블에는 주문 내역이, products 테이블에는 제품 정보가 저장되어 있다.
- 우리는 특정 고객이 주문한 상품들 중에서 가장 비싼 제품을 조회하려 한다.
예시 쿼리:
SELECT users.name, products.name, products.price
FROM users
INNER JOIN orders ON users.id = orders.user_id
INNER JOIN products ON orders.product_id = products.id
WHERE users.id = 1
ORDER BY products.price DESC
LIMIT 1;
- INNER JOIN: users, orders, products 테이블을 각각 user_id와 product_id를 기준으로 결합한다.
- WHERE 절: users.id = 1을 사용하여, 고객 1번의 주문 내역만 조회한다.
- ORDER BY: products.price를 기준으로 내림차순 정렬하여 가장 비싼 상품을 첫 번째로 출력한다.
- LIMIT 1: 가장 비싼 상품만 출력하도록 제한한다.
결과 예시:
name | name | price |
John Doe | Ultra Laptop X | 2000 |
이 결과는 고객 1번이 주문한 상품들 중 가장 비싼 상품을 반환한다. JOIN을 사용하여 고객, 주문, 상품을 결합하고, 가격이 가장 높은 상품을 조회한 예제이다.
JOIN과 서브쿼리를 함께 사용하면 복잡한 데이터 분석을 더욱 쉽게 수행할 수 있다. JOIN을 통해 여러 테이블을 결합하고, 그 후 서브쿼리를 통해 추가적인 조건을 처리하거나, 데이터를 필터링하는 방식은 SQL 쿼리를 강력하게 만들어 준다. 이러한 기법은 실제 데이터베이스에서 다양한 비즈니스 로직을 처리할 때 매우 유용하게 사용된다.
2025.03.28 - [Java] - 2025 정보처리기사 자격증 시험 소개, 응시자격, 시험일정, 접수 및 준비방법
2025 정보처리기사 자격증 시험 소개, 응시자격, 시험일정, 접수 및 준비방법
정보처리기사 자격증은 IT 분야 전문가로서의 기본적인 실력을 증명하는 국가기술자격증입니다. IT 시스템 개발, 운영, 분석 등 다양한 실무 능력을 평가하며, 취업 준비생뿐만 아니라 실무자에
it-learner.tistory.com