CTE(Common Table Expressions)의 활용법 – 서브쿼리보다 명확한 쿼리 작성

Posted by heoncode
2025. 4. 12. 11:49 SQL 기초 정리
728x90
반응형
SMALL

SQL에서 복잡한 쿼리를 작성할 때, 서브쿼리로만 해결하기 어려운 경우가 많습니다. 이럴 때 CTE (Common Table Expressions), 즉 WITH 구문을 사용하면 쿼리의 가독성과 유지보수를 크게 향상시킬 수 있습니다.

CTE의 기본 구조

CTE는 쿼리 상단에 WITH 절로 정의하며, 마치 임시 테이블처럼 재사용할 수 있습니다. 기본 구조는 다음과 같습니다.

WITH cte_name AS (
    SELECT ... FROM ...
)
SELECT * FROM cte_name;

첫 번째 부분에서 cte_name 이라는 이름의 CTE를 정의하고, 이후 메인 쿼리에서 이를 참조합니다.

CTE 사용의 장점

  1. 가독성 향상
    복잡한 로직을 여러 단계로 나누어 각각의 CTE로 정의하면, 전체 쿼리의 흐름을 한눈에 파악할 수 있어 디버깅과 협업에 유리합니다.

  2. 재사용성
    한 번 정의한 CTE를 메인 쿼리 내에서 여러 번 사용할 수 있으므로, 같은 로직을 반복 작성할 필요가 없습니다.

  3. 재귀 쿼리 작성
    CTE는 재귀적 호출을 지원하여, 계층적 데이터(예: 조직도나 카테고리 구조)를 쉽게 처리할 수 있습니다.

실무 예시: 고객별 최근 주문 내역과 누적 주문 수

아래 예시는 고객 테이블과 주문 테이블을 기반으로, 각 고객의 최근 주문 날짜와 누적 주문 수를 CTE로 먼저 계산한 후, 이를 메인 쿼리에서 결합하는 예시입니다.

WITH order_summary AS (
    SELECT customer_id,
           COUNT(*) AS total_orders,
           MAX(order_date) AS last_order_date
    FROM orders
    GROUP BY customer_id
)
SELECT c.customer_id, c.name,
       o.total_orders, o.last_order_date
FROM customers c
LEFT JOIN order_summary o ON c.customer_id = o.customer_id;

이 쿼리는 주문 집계 로직을 별도로 분리하여, 고객 테이블과 조인할 때 단순하게 결과를 얻을 수 있습니다.

마무리

CTE(Common Table Expressions)는 복잡한 쿼리를 구조화하고 가독성을 높이는 데 매우 유용한 도구입니다. 서브쿼리로 얽힌 코드를 단순화하고, 재사용 가능한 형태로 구현할 수 있으므로, 실무에서 적극 활용하는 것이 좋습니다.

#SQL #CTE #WITH구문 #공통테이블표현 #쿼리최적화 #SQL기초 #실무팁 #데이터베이스

728x90
반응형
LIST

COUNT(*)와 COUNT(컬럼명)의 차이 – 결과는 비슷해도 의미는 다르다

Posted by heoncode
2025. 4. 11. 17:17 SQL 기초 정리
728x90
반응형
SMALL

SQL에서 자주 사용하는 집계 함수 중 하나인 COUNT()는 단순히 행의 수를 세는 데에 유용합니다. 하지만 COUNT(*)COUNT(컬럼명)은 비슷해 보이지만 실제 의미와 동작 방식에서 중요한 차이를 가지고 있습니다.

COUNT(*)는 모든 행을 센다

COUNT(*)는 해당 테이블에서 NULL을 포함한 모든 행의 개수를 셉니다. 이는 테이블에 존재하는 전체 레코드 수를 파악하고자 할 때 사용됩니다.

예시:

SELECT COUNT(*) FROM employees;

이 쿼리는 employees 테이블의 총 행 수를 반환합니다. 특정 컬럼에 NULL이 있든 없든 관계없이 전체 행 수를 기준으로 계산합니다.

COUNT(컬럼명)는 NULL을 제외한다

반면 COUNT(컬럼명)은 해당 컬럼에서 NULL이 아닌 값의 개수만 셉니다. 즉, 어떤 컬럼의 값이 얼마나 채워져 있는지, 즉 실제 데이터가 있는 레코드 수를 알고 싶을 때 유용합니다.

예시:

SELECT COUNT(department_id) FROM employees;

이 쿼리는 department_id 컬럼의 값이 NULL이 아닌 행의 개수를 반환합니다. 만약 부서가 배정되지 않은 사원이 있다면 그 행은 집계에서 제외됩니다.

실무에서의 활용 팁

  • 데이터 완전성 검증 시에는 COUNT(컬럼명)을 사용하여 누락된 값이 있는지 파악합니다.
  • 전체 행 수가 필요한 로깅, 페이징 처리 등에서는 COUNT(*)을 사용하는 것이 적절합니다.
  • COUNT(DISTINCT 컬럼명)처럼 고유한 값의 개수를 세는 방식과도 구분하여 사용해야 합니다.

결론

COUNT(*)COUNT(컬럼명)은 비슷해 보이지만 전혀 다른 결과를 가져올 수 있으므로, 목적에 따라 정확히 구분하여 사용하는 것이 중요합니다. 특히 실무에서 NULL 값이 얼마나 존재하는지를 파악할 때 이 차이를 알고 있으면 보다 정확한 데이터 분석이 가능합니다.

#SQL #COUNT #집계함수 #기초정리 #데이터베이스 #NULL #쿼리

728x90
반응형
LIST

JOIN 없이도 가능하다? SQL에서 서브쿼리로 해결하는 3가지 실무 상황

Posted by heoncode
2025. 4. 11. 15:55 SQL 기초 정리
728x90
반응형
SMALL

SQL에서 데이터를 합치기 위해 가장 먼저 떠오르는 키워드는 JOIN입니다. 하지만 모든 상황에서 JOIN이 꼭 필요한 것은 아닙니다. 실무에서는 JOIN을 쓰기 복잡하거나 성능상 불리할 때, 서브쿼리(Subquery)를 이용해 문제를 해결하기도 합니다. 이번 글에서는 JOIN 없이 서브쿼리만으로 해결할 수 있는 3가지 실무 상황을 소개합니다.

1. 특정 조건에 해당하는 값만 가져올 때

예를 들어 각 사원의 부서 이름을 출력하고 싶은데, 특정 부서만 필터링하고 싶다면 JOIN 없이도 다음과 같이 서브쿼리로 처리할 수 있습니다.

SELECT emp_name,
       (SELECT dept_name
        FROM department d
        WHERE d.dept_id = e.dept_id)
       AS dept_name
FROM employee e
WHERE e.dept_id IN (10, 20);

JOIN을 사용하지 않고도 서브쿼리로 필요한 부서명을 함께 출력할 수 있습니다.

2. 조건 비교를 위해 필요한 보조 데이터를 가져올 때

각 제품의 가격이 전체 평균보다 높은 제품만 찾고 싶다면, 아래와 같이 서브쿼리를 WHERE 절에 활용할 수 있습니다.

SELECT product_name, price
FROM product
WHERE price > (SELECT AVG(price) FROM product);

이처럼 전체 집계 결과와 비교할 때도 서브쿼리를 사용하면 JOIN 없이 해결할 수 있습니다.

3. 최근 거래 내역 등 특정 조건의 1건만 가져올 때

각 고객의 최근 주문 내역을 보고 싶다면, 관련 테이블을 JOIN하기보다 상관 서브쿼리를 사용할 수 있습니다.

SELECT c.customer_id,
       (SELECT order_date
        FROM orders o
        WHERE o.customer_id = c.customer_id
        ORDER BY order_date DESC
        FETCH FIRST 1 ROW ONLY)
       AS latest_order
FROM customer c;

이 방식은 고객 수가 많아도 간결하고 효율적인 쿼리를 작성할 수 있게 해줍니다.


JOIN은 강력한 도구지만, 서브쿼리는 구조를 단순화하거나 불필요한 조인을 줄이는 데 유용하게 쓰일 수 있습니다. 실무에서는 복잡한 조인보다 오히려 서브쿼리로 명확하게 표현하는 것이 유지보수나 성능 측면에서 더 나은 선택이 되는 경우도 많습니다.

#SQL #서브쿼리 #JOIN #실무팁 #쿼리작성 #DB초보 #SQL기초

728x90
반응형
LIST

LEFT JOIN 결과가 예상과 다를 때? 누락된 데이터의 진짜 원인

Posted by heoncode
2025. 4. 11. 00:38 SQL 기초 정리
728x90
반응형
SMALL

SQL에서 LEFT JOIN왼쪽 테이블의 모든 데이터를 기준으로 오른쪽 테이블과 연결해주는 기능입니다. 하지만 실무에서는 종종 LEFT JOIN을 썼는데도 결과가 예상보다 적게 나오는 일이 발생합니다. 이 글에서는 그런 상황에서 확인해야 할 3가지 핵심 원인을 소개합니다.

1. WHERE 절 조건이 JOIN 결과를 제한한다

아래 쿼리를 살펴봅시다:

SELECT *
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
WHERE d.location = 'SEOUL';

의도는 모든 직원 정보를 보고 싶은 것인데, 이 쿼리는 d.location이 NULL인 직원(=부서가 없는 직원)은 제외됩니다. 이는 WHERE 절이 조인 이후에 적용되기 때문입니다.

→ 해결책은 조건을 JOIN 안에 넣는 것입니다:

SELECT *
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id AND d.location = 'SEOUL';

이렇게 하면 부서가 없는 직원도 빠지지 않습니다.

2. JOIN 조건이 누락되거나 잘못되었다

SELECT *
FROM employees e
LEFT JOIN departments d ON e.name = d.name;

이런 식으로 정확한 조인 조건 없이 컬럼 이름만 같은 걸 기준으로 조인하면 원하지 않는 결과가 나옵니다. 항상 기본키-외래키 관계 또는 고유한 값을 기준으로 조인해야 합니다.

3. 중첩 JOIN과 서브쿼리에서 발생하는 필터링

복잡한 쿼리일수록 LEFT JOIN이 서브쿼리 내부에 있거나 INNER JOIN과 함께 쓰이면서 무의식적으로 결과가 줄어들 수 있습니다. 쿼리 구조를 항상 시각화하면서 데이터 흐름을 따져보는 습관이 필요합니다.


LEFT JOIN은 단순한 듯 보여도 실무에서는 자주 함정에 빠지는 구간입니다. 특히 WHERE 절의 위치나 조인 조건에 따라 결과가 확 달라질 수 있으니 항상 주의 깊게 쿼리를 작성해야 합니다.


#SQL #JOIN #LEFTJOIN #외부조인 #누락 #쿼리디버깅 #기초정리 #실무팁

728x90
반응형
LIST

SELECT *를 쓰면 왜 안 좋을까? 실무에서 컬럼을 명시하는 이유

Posted by heoncode
2025. 4. 10. 17:11 SQL 기초 정리
728x90
반응형
SMALL

SQL을 처음 배울 때 가장 먼저 접하게 되는 문장이 SELECT * FROM 테이블명입니다. 모든 컬럼을 조회할 수 있어 편리하지만, 실무에서는 대부분의 경우 SELECT * 사용을 지양합니다. 왜 그럴까요?

1. 성능 저하

SELECT *는 테이블의 모든 컬럼을 조회하므로, 필요한 컬럼만 선택한 경우보다 훨씬 많은 데이터를 읽게 됩니다. 특히 테이블에 수십 개의 컬럼이 있을 경우, 불필요한 IO 작업이 증가하고 네트워크 부하도 커지게 됩니다.

2. 유지보수의 어려움

테이블 구조가 변경되면 SELECT *의 결과도 바뀔 수 있습니다. 예를 들어 새 컬럼이 추가되었을 때, 의도치 않게 쿼리 결과가 달라져 에러가 발생하거나 보고서 양식이 깨지는 등의 문제가 생길 수 있습니다.

3. 가독성과 명확성 부족

어떤 컬럼이 사용되는지 코드만 보고는 알 수 없습니다. 반면 컬럼을 명시하면 해당 쿼리가 어떤 데이터를 다루는지 명확히 알 수 있어 유지보수와 협업에 유리합니다.

4. 인덱스 무효화 가능성

인덱스를 활용하는 쿼리의 경우, SELECT *로 인해 인덱스만으로는 데이터를 충족할 수 없어 테이블 전체를 읽는 Full Table Scan이 발생할 수 있습니다.

정리하자면

가능한 한 필요한 컬럼만 명시하는 습관을 들이는 것이 좋습니다. 예를 들어:

SELECT emp_id, emp_name, salary
FROM employees;

이렇게 쓰면 성능도 좋고, 명확성도 올라가며 유지보수도 쉬워집니다. SELECT *는 학습 단계에선 도움이 되지만, 실무에선 피해야 할 문장입니다.


#SQL #SELECT문 #SELECT별표 #쿼리작성 #성능 #실무팁 #데이터베이스 #기초정리

728x90
반응형
LIST

NULL은 0이 아니다! SQL에서 NULL이 가진 의미와 주의점

Posted by heoncode
2025. 4. 10. 11:34 SQL 기초 정리
728x90
반응형
SMALL

SQL을 처음 접할 때 가장 혼동하기 쉬운 개념 중 하나가 바로 NULL입니다. 많은 분들이 NULL을 숫자 0이나 빈 문자열로 오해하지만, 실제로는 값이 존재하지 않음을 의미합니다.

NULL은 어떤 의미인가요?

  • NULL값이 존재하지 않음을 나타냅니다.
  • 숫자 0(0)은 실제 값이고, NULL값 자체가 없음입니다.
  • 빈 문자열('')도 NULL과 다릅니다. 빈 문자열은 값이지만, 비어있는 값일 뿐입니다.

NULL을 비교할 때 주의할 점

일반적인 비교 연산자(=, !=)로는 NULL 값을 정확히 비교할 수 없습니다. 예를 들어 다음 쿼리는 아무 결과도 반환하지 않습니다.

SELECT * FROM employees WHERE commission_pct = NULL;

이유는 NULL = NULL은 참이 아니라 알 수 없음(UNKNOWN)이 되기 때문입니다. NULL 비교는 반드시 IS NULL 또는 IS NOT NULL을 사용해야 합니다.

SELECT * FROM employees WHERE commission_pct IS NULL;

집계 함수와 NULL

  • COUNT(*)는 모든 행을 세지만
    COUNT(column)은 해당 컬럼이 NULL이 아닌 값만 셉니다.
  • SUM, AVG, MAX, MIN 같은 집계 함수도 NULL은 무시하고 계산합니다.

실무에서 자주 발생하는 실수

  • 조건절에 = NULL 사용 → 항상 결과가 없음
  • 조인 시 NULL값이 있는 컬럼 누락 → 원하는 결과 누락
  • 정렬(SORT) 결과가 예상과 다름 → NULLS FIRST 또는 NULLS LAST 명시 필요

SQL에서 NULL은 단순한 "빈 값"이 아닌, 존재하지 않는 상태로 간주됩니다. 쿼리 작성 시 NULL의 의미를 정확히 이해하고 조건절이나 집계 함수에서 적절히 처리하는 것이 매우 중요합니다.


#SQL #NULL #값비교 #ISNULL #쿼리실수 #기초정리 #데이터베이스

728x90
반응형
LIST

INNER JOIN과 OUTER JOIN, 뭐가 다를까? 실무에서 꼭 알아야 할 차이점

Posted by heoncode
2025. 4. 10. 00:57 SQL 기초 정리
728x90
반응형
SMALL

INNER JOIN과 OUTER JOIN, 뭐가 다를까? 실무에서 꼭 알아야 할 차이점

SQL에서 여러 테이블의 데이터를 함께 조회할 때 JOIN을 사용합니다. 하지만 JOIN에는 여러 종류가 있어서 처음 접하는 분들은 헷갈리기 쉽습니다. 특히 INNER JOINOUTER JOIN의 차이를 정확히 이해하는 것은 실무에서 매우 중요합니다. 이번 글에서는 이 두 JOIN의 차이점과 사용 예시를 통해 개념을 확실히 잡아보겠습니다.

1. INNER JOIN – 교집합 찾기

INNER JOIN은 두 테이블에서 공통된 값이 있는 행만 반환합니다. 즉, 양쪽 테이블에 모두 존재하는 데이터만 조회하고, 한쪽에만 있는 데이터는 제외됩니다.

예제:

employees 테이블과 departments 테이블이 있다고 가정해봅시다.

  • employees 테이블:

    employee_id name department_id
    1 철수 10
    2 영희 20
    3 민수 30
  • departments 테이블:

    department_id department_name
    10 인사부
    20 개발부
    40 마케팅부

이제 두 테이블을 department_id를 기준으로 INNER JOIN 해보겠습니다.

SELECT e.name, d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;

결과:

| name  | department_name |
|-------|-----------------|
| 철수  | 인사부          |
| 영희  | 개발부          |

여기서 민수마케팅부는 각각 다른 테이블에만 존재하므로 결과에서 제외되었습니다.

2. OUTER JOIN – 전체 데이터 조회하기

OUTER JOIN은 한쪽 테이블에만 존재하는 데이터도 포함하여 조회합니다. OUTER JOIN은 다시 LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN으로 나뉩니다.

  • LEFT OUTER JOIN: 왼쪽 테이블의 모든 행을 반환하고, 오른쪽 테이블에 일치하는 데이터가 없으면 NULL을 반환합니다.
  • RIGHT OUTER JOIN: 오른쪽 테이블의 모든 행을 반환하고, 왼쪽 테이블에 일치하는 데이터가 없으면 NULL을 반환합니다.
  • FULL OUTER JOIN: 양쪽 테이블의 모든 행을 반환하며, 일치하는 데이터가 없으면 NULL을 반환합니다.

예제:

위의 employeesdepartments 테이블을 LEFT OUTER JOIN 해보겠습니다.

SELECT e.name, d.department_name
FROM employees e
LEFT OUTER JOIN departments d ON e.department_id = d.department_id;

결과:

| name  | department_name |
|-------|-----------------|
| 철수  | 인사부          |
| 영희  | 개발부          |
| 민수  | NULL            |

민수employees 테이블에는 있지만 departments 테이블에 해당 department_id가 없으므로 department_name이 NULL로 표시됩니다.

✅ 마무리

  • INNER JOIN: 양쪽 테이블에 모두 존재하는 데이터만 조회
  • OUTER JOIN:
    • LEFT OUTER JOIN: 왼쪽 테이블의 모든 데이터와 일치하는 오른쪽 테이블의 데이터 조회
    • RIGHT OUTER JOIN: 오른쪽 테이블의 모든 데이터와 일치하는 왼쪽 테이블의 데이터 조회
    • FULL OUTER JOIN: 양쪽 테이블의 모든 데이터 조회

JOIN을 적절히 사용하면 데이터베이스에서 원하는 정보를 효율적으로 추출할 수 있습니다. 실무에서는 데이터의 특성과 요구사항에 맞게 JOIN을 선택하여 사용해야 합니다.

#SQL #JOIN #INNERJOIN #OUTERJOIN #데이터베이스 #기초문법 #실무팁

728x90
반응형
LIST

인덱스가 성능을 좌우한다? 속도 차이가 극명하게 나는 상황 3가지

Posted by heoncode
2025. 4. 8. 16:52 오라클 실무 쿼리 튜닝
728x90
반응형
SMALL

인덱스가 성능을 좌우한다? 속도 차이가 극명하게 나는 상황 3가지

SQL 성능 튜닝에서 인덱스(Index)는 빼놓을 수 없는 핵심 요소입니다. 하지만 단순히 인덱스를 만든다고 해서 항상 쿼리 속도가 빨라지는 것은 아닙니다. 인덱스가 실제로 효과를 발휘하는 조건과, 오히려 비효율을 유발하는 상황까지 모두 고려해야 실무에서 제대로 활용할 수 있습니다.

이번 글에서는 인덱스 유무에 따라 성능 차이가 극명하게 나는 3가지 상황을 실무 관점에서 정리해봅니다.


1. WHERE 절에서 다중 조건 필터링 시

SELECT * FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10 AND JOB_ID = 'IT_PROG';

만약 DEPARTMENT_ID, JOB_ID 각각 단독 인덱스만 존재하고, 두 컬럼을 동시에 포함한 복합 인덱스가 없다면 옵티마이저는 인덱스를 비효율적으로 사용할 수 있습니다. 반면 (DEPARTMENT_ID, JOB_ID)로 구성된 복합 인덱스를 만들면 효율적인 Index Range Scan이 가능해집니다.

📌 실무 팁: 자주 함께 사용하는 조건은 복합 인덱스로 묶어주는 것이 훨씬 유리합니다.


2. 정렬(ORDER BY) + 페이징 처리 시

SELECT * FROM EMPLOYEES
ORDER BY HIRE_DATE
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

HIRE_DATE 컬럼에 인덱스가 없으면 정렬 작업은 전 테이블을 스캔하면서 정렬 메모리를 소모하게 됩니다. 반면 인덱스가 있으면 인덱스 자체가 이미 정렬된 구조이므로 빠르게 필요한 범위만 추출할 수 있습니다.

📌 실무 팁: 페이징 쿼리에서는 정렬 기준 컬럼에 반드시 인덱스가 있어야 성능 이점을 누릴 수 있습니다.


3. 서브쿼리에서 EXISTS 조건 사용 시

SELECT * FROM DEPARTMENTS D
WHERE EXISTS (
  SELECT 1 FROM EMPLOYEES E
  WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
);

EMPLOYEES.DEPARTMENT_ID에 인덱스가 없다면 EXISTS 서브쿼리마다 테이블을 반복 스캔하게 되어 성능 저하가 큽니다. 반면 해당 컬럼에 인덱스가 있다면 각 부서별 매칭을 빠르게 찾을 수 있어 성능이 크게 개선됩니다.

📌 실무 팁: EXISTS 또는 IN 서브쿼리에서 조인 키에 인덱스가 없으면 심각한 성능 저하가 발생할 수 있습니다.


마무리하며

인덱스는 단순히 만들어두는 것이 아니라, 쿼리 실행 계획을 고려한 설계가 중요합니다. 특히 자주 사용되는 조합, 정렬 조건, 서브쿼리의 조인 키 등을 파악해 전략적으로 인덱스를 설계하는 것이 실무 튜닝의 핵심입니다.

항상 실행 계획(EXPLAIN PLAN)을 확인하고, 인덱스가 실제로 사용되는지 검증하는 습관을 들이세요.

#오라클 #SQL #쿼리튜닝 #인덱스 #성능개선 #실무팁 #데이터베이스

728x90
반응형
LIST